diff --git a/ide/src/base-ui/select/LitSelectHtml.ts b/ide/src/base-ui/select/LitSelectHtml.ts index 785ebaa12f57fa1d560dee38b7529ca21feabc04..069c8603d84d921aa75fe88a05a27a71b1e5e81f 100644 --- a/ide/src/base-ui/select/LitSelectHtml.ts +++ b/ide/src/base-ui/select/LitSelectHtml.ts @@ -258,7 +258,7 @@ export const selectVHtmlStr = ` position: relative; overflow: visible; cursor: pointer; - border-radius: 2px; + border-radius: 16px; outline: none; -webkit-user-select:none ; -moz-user-select:none; diff --git a/ide/src/base-ui/table/lit-table.ts b/ide/src/base-ui/table/lit-table.ts index 167485fff2ce473bb0d4af9db1c3865d6c3586fc..dfc1d337cab76de3c18ee73dd10b5c3a5c879e9a 100644 --- a/ide/src/base-ui/table/lit-table.ts +++ b/ide/src/base-ui/table/lit-table.ts @@ -67,6 +67,7 @@ export class LitTable extends HTMLElement { private _mode = TableMode.Expand; private columnResizeEnable: boolean = true; private _isSearch: boolean = false; + private maxLength: number = 0; constructor() { super(); @@ -164,6 +165,32 @@ export class LitTable extends HTMLElement { } set recycleDataSource(value) { + // 处理数据按小数点位置对齐 + if (value && value.length) { + // 找出数字部分的最大长度 + value.forEach((item: unknown) => { + // 提取数字部分(包括小数点) + // @ts-ignore + if (item.durFormat) { + // @ts-ignore + const match = item.durFormat.match(/^(\d+(\.\d+)?)/); + if (match && match[1]) { + // 计算长度(包括小数点) + const length = match[1].length; + this.maxLength = Math.max(this.maxLength, length); + } + } + // @ts-ignore + if (item.percent) { + // @ts-ignore + const match = item.percent.match(/^(\d+(\.\d+)?)/); + if (match && match[1]) { + const length = match[1].length; + this.maxLength = Math.max(this.maxLength, length); + } + } + }); + } if (this.tableElement) { this.isScrollXOutSide = this.tableElement!.scrollWidth > this.tableElement!.clientWidth; this.isRecycleList = true; @@ -1583,6 +1610,28 @@ export class LitTable extends HTMLElement { if (rowObject.data.rowName === 'cpu-profiler' && dataIndex === 'symbolName') { (child as HTMLElement).innerHTML = ''; } else { + //@ts-ignore + if (rowObject.data.durFormat) { //ebpf泳道下的analysis页 + // 提取数字部分(包括小数点) + if (dataIndex === 'durFormat') { + // @ts-ignore + const match = text.match(/^(\d+(\.\d+)?)(.*)$/); + if (match && match[1] && match[3]) { + // 计算需要添加的空格数 + const padding = '\xa0\xa0'.repeat(this.maxLength - match[1].length); + // 构造新的durFormat字符串 + text = padding + match[1] + match[3]; + } + } + if (dataIndex === 'percent') { + // @ts-ignore + const match = text.match(/^(\d+(\.\d+)?)(.*)$/); + if (match && match[1]) { + const padding = '\xa0\xa0'.repeat(this.maxLength - match[1].length); + text = padding + match[1]; + } + } + } //@ts-ignore (child as HTMLElement).innerHTML = text; } //@ts-ignore diff --git a/ide/src/trace/bean/BoxSelection.ts b/ide/src/trace/bean/BoxSelection.ts index d8b22b6d4454d88c3f3314e029a2ababa9019e77..644b4037649361ab06f83f6083e59afbee52810f 100644 --- a/ide/src/trace/bean/BoxSelection.ts +++ b/ide/src/trace/bean/BoxSelection.ts @@ -69,6 +69,7 @@ export class SelectionParam { ((arg: unknown) => Promise> | undefined) | undefined >(); dmaFenceNameData: Array = [];//新增框选dma_fence数据 + hangMapData: Map Promise> | undefined) | undefined> = new Map(); irqCallIds: Array = []; softIrqCallIds: Array = []; funTids: Array = []; @@ -1136,6 +1137,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 || it.rowType === TraceRow.ROW_TYPE_HANG_INNER) { + this.hangMapData.set(it.rowId || '', it.getCacheData); + } + } + // @ts-ignore pushGpuMemoryVmTracker(it: TraceRow, sp: SpSystemTrace): void { if (it.rowType === TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER) { @@ -1246,6 +1261,7 @@ export class SelectionParam { this.pushVmTrackerShm(it, sp); this.pushClock(it, sp); this.pushDmaFence(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 980162198ebd68e6d27271b26bbbb683d90edb23..3e511e95027dce6d1687916b5089e4025d6b8c6c 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -26,9 +26,10 @@ const CAT_SORT = { 'Thread first': 'thread' }; -const CONFIG_STATE = { +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') @@ -82,22 +83,27 @@ export class SpFlags extends BaseElement { configDiv.appendChild(description); } //监听flag-select的状态选择 - private flagSelectListener(configSelect: unknown): void { + private flagSelectListener(configSelect: HTMLSelectElement): void { // @ts-ignore let title = configSelect.getAttribute('title'); - let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title as keyof typeof CONFIG_STATE][0]}`); + + //@ts-ignore + let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title]?.[0]}`); // @ts-ignore FlagsConfig.updateFlagsConfig(title!, configSelect.selectedOptions[0].value); - if (CONFIG_STATE[title as keyof typeof CONFIG_STATE]) { + //@ts-ignore + if (listSelect) { // @ts-ignore if (configSelect.selectedOptions[0].value === 'Enabled') { listSelect?.removeAttribute('disabled'); } else { listSelect?.childNodes.forEach((child: ChildNode) => { let selectEl = child as HTMLOptionElement; - if (child.textContent === CONFIG_STATE[title as keyof typeof CONFIG_STATE][1]) { + //@ts-ignore + if (child.textContent === CONFIG_STATE[title]?.[1]) { selectEl.selected = true; - FlagsConfig.updateFlagsConfig(CONFIG_STATE[title as keyof typeof CONFIG_STATE][0], selectEl.value); + //@ts-ignore + FlagsConfig.updateFlagsConfig(CONFIG_STATE[title]?.[0], selectEl.value); } else { selectEl.selected = false; } @@ -152,17 +158,24 @@ export class SpFlags extends BaseElement { } if (config.title === 'VSync') { - let configKey = CONFIG_STATE['VSync' as keyof typeof CONFIG_STATE][0]; + //@ts-ignore + let configKey = CONFIG_STATE[config.title]?.[0]; let configFooterDiv = this.createPersonOption(VSYNC_VAL, configKey, config.addInfo!.vsyncValue, config.title); configDiv.appendChild(configFooterDiv); } if (config.title === 'Start&Finish Trace Category') { - let configKey = CONFIG_STATE['Start&Finish Trace Category' as keyof typeof CONFIG_STATE][0]; + //@ts-ignore + let configKey = CONFIG_STATE[config.title]?.[0]; let configFooterDiv = this.createPersonOption(CAT_SORT, configKey, config.addInfo!.catValue, config.title); configDiv.appendChild(configFooterDiv); } + if (config.title === 'Hangs') { + let configFooterDiv = this.createHangsOption(); + configDiv.appendChild(configFooterDiv); + } + this.bodyEl!.appendChild(configDiv); }); } @@ -207,6 +220,51 @@ export class SpFlags extends BaseElement { configFooterDiv.appendChild(vsyncTypeEl); return configFooterDiv; } + + /// Flags新增Hangs下拉框 + private createHangsOption(): HTMLDivElement { + 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 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', hangOptions[0].value); + hangOptions[0].selected = true; + hangsTypeEl.addEventListener('change', function () { + let selectValue = 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'); + } + configFooterDiv.appendChild(hangsLableEl); + configFooterDiv.appendChild(hangsTypeEl); + return configFooterDiv; + } } export type Params = { @@ -258,6 +316,11 @@ export class FlagsConfig { describeContent: 'VSync Signal drawing', addInfo: { vsyncValue: VSYNC_VAL.VsyncGeneratior }, }, + { + 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 e633f754b21990abb465489f026181b1eb7d67f2..a187e5a45e82538093e4d67748a0cdb8fc1bf7d9 100644 --- a/ide/src/trace/component/SpSystemTrace.event.ts +++ b/ide/src/trace/component/SpSystemTrace.event.ts @@ -52,6 +52,7 @@ import { PerfToolsStructOnClick, PerfToolStruct } from '../database/ui-worker/Pr import { Utils } from './trace/base/Utils'; import { BaseStruct } from '../bean/BaseStruct'; import { GpuCounterStruct, gpuCounterStructOnClick } from '../database/ui-worker/ProcedureWorkerGpuCounter'; +import { HangStructOnClick } from '../database/ui-worker/ProcedureWorkerHang'; function timeoutJudge(sp: SpSystemTrace): number { let timeoutJudge = window.setTimeout((): void => { @@ -368,6 +369,7 @@ function allStructOnClick(clickRowType: string, sp: SpSystemTrace, row?: TraceRo .then(() => CpuStateStructOnClick(clickRowType, sp, entry as CpuStateStruct)) .then(() => CpuFreqLimitsStructOnClick(clickRowType, sp, entry as CpuFreqLimitsStruct)) .then(() => ClockStructOnClick(clickRowType, sp, entry as ClockStruct)) + .then(() => HangStructOnClick(clickRowType, sp)) .then(() => DmaFenceStructOnClick(clickRowType, sp, entry as DmaFenceStruct)) .then(() => SnapshotStructOnClick(clickRowType, sp, row as TraceRow, entry as SnapshotStruct)) .then(() => IrqStructOnClick(clickRowType, sp, entry as IrqStruct)) diff --git a/ide/src/trace/component/SpSystemTrace.init.ts b/ide/src/trace/component/SpSystemTrace.init.ts index 6a73dbc21acfed425426551e3cb6331d91e56a71..cabe4017cba2d6c3012c4df065d1c9972241a20a 100644 --- a/ide/src/trace/component/SpSystemTrace.init.ts +++ b/ide/src/trace/component/SpSystemTrace.init.ts @@ -1120,6 +1120,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/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index 74c893600bd83d8aae2dfab66f21c91cf61dec06..dfac1a593d20a200e1c5231d58d389200f10f069 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -132,6 +132,7 @@ import { PerfToolStruct } from '../database/ui-worker/ProcedureWorkerPerfTool'; import { BaseStruct } from '../bean/BaseStruct'; import { GpuCounterStruct } from '../database/ui-worker/ProcedureWorkerGpuCounter'; import { SpProcessChart } from './chart/SpProcessChart'; +import { HangStruct } from '../database/ui-worker/ProcedureWorkerHang'; function dpr(): number { return window.devicePixelRatio || 1; @@ -348,6 +349,7 @@ export class SpSystemTrace extends BaseElement { for (let i = 0; i < flagList.length; i++) { if (flagList[i].type === 'triangle') { flagList.splice(i, 1); + this.timerShaftELFlagChange(this.hoverFlag, null); i--; } } @@ -355,8 +357,10 @@ export class SpSystemTrace extends BaseElement { pushPidToSelection(selection: SelectionParam, id: string): void { let pid = parseInt(id); - if (!selection.processIds.includes(pid)) { - selection.processIds.push(pid); + if (!isNaN(pid)) { + if (!selection.processIds.includes(pid)) { + selection.processIds.push(pid); + } } } // @ts-ignore @@ -1219,6 +1223,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 b6d0b22303576f972d80a8bb8801600831b6533c..4acfe42ab58248a39c5ad5060bed0d86a16fdf41 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, @@ -86,6 +87,7 @@ export class SpChartManager { private logChart: SpLogChart; private spHiSysEvent: SpHiSysEventChart; private spSegmentationChart: SpSegmentationChart; + private hangChart: SpHangChart; private spBpftraceChart: SpBpftraceChart; private spPerfOutputDataChart: SpPerfOutputDataChart; private spGpuCounterChart: SpGpuCounterChart; @@ -114,6 +116,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.spGpuCounterChart = new SpGpuCounterChart(trace); @@ -186,6 +189,10 @@ export class SpChartManager { await this.spHiSysEvent.init(); let idAndNameArr = await queryDmaFenceIdAndCat(); this.handleDmaFenceName(idAndNameArr as { id: number; cat: string; seqno: number; driver: string; context: string }[]); + 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 new file mode 100644 index 0000000000000000000000000000000000000000..a8f6b2e13a7ab2c5289f163164b55ea0019424a0 --- /dev/null +++ b/ide/src/trace/component/chart/SpHangChart.ts @@ -0,0 +1,192 @@ +/* + * 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 { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; +import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; +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' | ''; + +/// Hangs聚合泳道 +export class SpHangChart { + private trace: SpSystemTrace; + static 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 { + SpHangChart.funcNameMap = Utils.getInstance().getCallStatckMap(); + 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 = hangDataSender(it.id, traceRow); + if (promiseData === null) { + return new Promise>((resolve) => resolve([])); + } else { + return promiseData.then((resultHang: Array) => + resultHang.map(hangItem => ({ + ...hangItem, + pname: it.name, + type: SpHangChart.calculateHangType(hangItem.dur!), + content: SpHangChart.funcNameMap.get(hangItem.id!) + })) + ); + } + }; + } + + 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.renderMainThread( + { + context: context, + useCache: useCache, + type: it.name, + index: hangId, + }, + traceRow + ); + traceRow.canvasRestore(context, this.trace); + }; + } + + /// 初始化聚合泳道信息 + async initData(folder: TraceRow): Promise { + let hangStartTime = new Date().getTime(); + let hangList = await queryHangData(); + if (hangList.length === 0) { + return; + } + this.trace.rowsEL?.appendChild(folder); + for (let i = 0; i < hangList.length; i++) { + const it: { + id: number, + name: string, + 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.focusHandler = (ev): void => { + 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); + } + 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/chart/SpProcessChart.ts b/ide/src/trace/component/chart/SpProcessChart.ts index e558a6243d4166a004d70bbec457b427a6c0e21c..92bb4a38871a7c5aa4e3b30566033491b5759789 100644 --- a/ide/src/trace/component/chart/SpProcessChart.ts +++ b/ide/src/trace/component/chart/SpProcessChart.ts @@ -54,6 +54,10 @@ import { import { queryAllJankProcess } from '../../database/sql/Janks.sql'; import { BaseStruct } from '../../bean/BaseStruct'; import { promises } from 'dns'; +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'; const FOLD_HEIGHT = 24; export class SpProcessChart { @@ -93,6 +97,7 @@ export class SpProcessChart { static threadStateList: Map = new Map(); static processRowSortMap: Map = new Map(); + private hangProcessSet: Set = new Set(); constructor(trace: SpSystemTrace) { this.trace = trace; } @@ -419,8 +424,13 @@ export class SpProcessChart { } 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, traceId); let durTime = new Date().getTime() - time; @@ -510,12 +520,14 @@ export class SpProcessChart { traceId?: string ): 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; @@ -549,17 +561,47 @@ export class SpProcessChart { } } this.renderRow = null; //@ts-ignore + if (this.loadAppStartup) { + if (this.startupProcessArr.find((sp) => sp.pid === it.pid)) { + startupRow = this.addStartUpRow(processRow); + } + let maxSoDepth = this.processSoMaxDepth.find((md) => md.pid === it.pid); + if (maxSoDepth) { + soRow = this.addSoInitRow(processRow, maxSoDepth.maxDepth); + } + } + + let hangsRow: TraceRow | null = null; + this.renderRow = null; if (it.processName === 'render_service') { //@ts-ignore this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow, traceId); //@ts-ignore this.addProcessMemInfo(it, processRow); //@ts-ignore this.addAsyncFunction(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); + } + hangsRow = this.addHangRow(it, processRow); + this.addProcessRowListener(processRow, actualRow); + this.addAsyncFunction(it, processRow); this.addAsyncCatFunction(it, processRow); } else { //@ts-ignore this.addAsyncFunction(it, processRow); //@ts-ignore this.addProcessMemInfo(it, processRow); //@ts-ignore this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow, traceId);//@ts-ignore + if (jankArr.indexOf(it.pid!) > -1) { + expectedRow = this.addExpectedRow(it, processRow, rsProcess); + actualRow = this.addActualRow(it, processRow, rsProcess); + } + 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); } this.addProcessRowListener(processRow, actualRow); @@ -641,7 +683,6 @@ export class SpProcessChart { this.trace.linkNodes.forEach((linkNodeItem) => this.handler3(e, linkNodeItem)); }, 300); } else { - FuncStruct.selectLineFuncStruct.push(FuncStruct.selectFuncStruct); offsetYTimeOut = setTimeout(() => { this.trace.linkNodes?.forEach((linkProcessItem) => { this.handler4(e, linkProcessItem, processRow); @@ -857,12 +898,70 @@ 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: SpHangChart.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, process: unknown, row: TraceRow, - renderServiceProcess: Array + renderServiceProcess: Array, ): void { let maxDepth: number = 1; let unitHeight: number = 20; @@ -998,6 +1097,7 @@ export class SpProcessChart { pRow: TraceRow, expectedRow: TraceRow | null, actualRow: TraceRow | null, + hangsRow: TraceRow | null, soRow: TraceRow | undefined, startupRow: TraceRow | undefined, traceId?: string @@ -1041,7 +1141,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; @@ -1079,6 +1179,7 @@ export class SpProcessChart { actualRow: TraceRow | null, //@ts-ignore expectedRow: TraceRow | null, + hangsRow: TraceRow | null, startupRow: TraceRow | null | undefined, soRow: TraceRow | null | undefined ): void { @@ -1097,6 +1198,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/setting/SpAllocations.ts b/ide/src/trace/component/setting/SpAllocations.ts index 52bda968653a19e998d0c602b736c9df8648776f..091d12413643ec591998f20ff0e089dd9fb89778 100644 --- a/ide/src/trace/component/setting/SpAllocations.ts +++ b/ide/src/trace/component/setting/SpAllocations.ts @@ -518,7 +518,7 @@ export class SpAllocations extends BaseElement { Cmd.getProcess().then((processList: string[]): void => { this.processId?.dataSource(processList, ''); if (processList.length > 0) { - this.processId?.dataSource(processList, 'ALL-Process'); + this.processId?.dataSource(processList, ''); } else { this.processId?.dataSource([], ''); } diff --git a/ide/src/trace/component/setting/SpArkTs.html.ts b/ide/src/trace/component/setting/SpArkTs.html.ts index ff4744845051125c41f7a71b4fc64686a80a0dd7..142fa8b46c74182a0e7666b6da479f02d47bf9ee 100644 --- a/ide/src/trace/component/setting/SpArkTs.html.ts +++ b/ide/src/trace/component/setting/SpArkTs.html.ts @@ -131,8 +131,8 @@ lit-switch { Process Record process - +
diff --git a/ide/src/trace/component/setting/SpArkTs.ts b/ide/src/trace/component/setting/SpArkTs.ts index 94629c1d04c76a9bf76dc4a1192eee7e70f15aca..fb993c35df59ffaea381d077da12a1ecf6898ce6 100644 --- a/ide/src/trace/component/setting/SpArkTs.ts +++ b/ide/src/trace/component/setting/SpArkTs.ts @@ -25,10 +25,11 @@ import { SpCheckDesBox } from './SpCheckDesBox'; import LitSwitch from '../../../base-ui/switch/lit-switch'; import { SpApplication } from '../../SpApplication'; import { SpArkTsHtml } from './SpArkTs.html'; +import { LitSelectV } from '../../../base-ui/select/LitSelectV'; @element('sp-ark-ts') export class SpArkTs extends BaseElement { - private processInput: LitAllocationSelect | undefined | null; + private processInput: LitSelectV | undefined | null; private spCheckDesBox: SpCheckDesBox | undefined | null; private radioBox: LitRadioBox | undefined | null; private interval: HTMLInputElement | undefined | null; @@ -111,24 +112,31 @@ export class SpArkTs extends BaseElement { initElements(): void { this.interval = this.shadowRoot?.querySelector('#interval'); - this.processInput = this.shadowRoot?.querySelector('lit-allocation-select'); - let processInput = this.processInput?.shadowRoot?.querySelector('.multipleSelect') as HTMLDivElement; + this.processInput = this.shadowRoot?.querySelector('lit-select-v'); + let processInput = this.processInput?.shadowRoot?.querySelector('input') as HTMLDivElement; this.cpuSwitch = this.shadowRoot?.querySelector('#cpu-switch') as LitSwitch; processInput!.addEventListener('mousedown', () => { - if (SpRecordTrace.serialNumber === '') { - this.processInput!.processData = []; - this.processInput!.initData(); + if (this.startSamp && (SpRecordTrace.serialNumber === '')) { + this.processInput!.dataSource([], ''); } }); processInput!.addEventListener('mouseup', () => { - if (SpRecordTrace.serialNumber === '') { - this.processInput!.processData = []; - this.processInput!.initData(); + if (this.startSamp) { + if (SpRecordTrace.serialNumber === '') { + this.processInput!.dataSource([], ''); + } else { + Cmd.getDebugProcess().then((processList) => { + if (processList.length > 0) { + this.processInput!.dataSource(processList, ''); + } else { + this.processInput!.dataSource([], ''); + } + }); + } + processInput!.removeAttribute('readonly'); } else { - Cmd.getDebugProcess().then((processList) => { - this.processInput!.processData = processList; - this.processInput!.initData(); - }); + processInput!.setAttribute('readonly', 'readonly'); + return; } }); this.litSwitch = this.shadowRoot?.querySelector('lit-switch') as LitSwitch; diff --git a/ide/src/trace/component/setting/SpFFRTConfig.ts b/ide/src/trace/component/setting/SpFFRTConfig.ts index 86c6e66c53c55c1d162aa7724b5d3c98161d9aa8..fc23522b80c385126f4d16908ed998431f8dd048 100644 --- a/ide/src/trace/component/setting/SpFFRTConfig.ts +++ b/ide/src/trace/component/setting/SpFFRTConfig.ts @@ -195,7 +195,6 @@ export class SpFFRTConfig extends BaseElement { Cmd.getPackage().then((packageList: string[]): void => { let finalDataList = packageList.map(str => str.replace(/\t/g, '')); if (finalDataList.length > 0) { - processInputEl.readOnly = true; startupPNameEl.dataSource(finalDataList, 'ALL-Process'); } else { startupPNameEl.dataSource([], ''); @@ -261,7 +260,6 @@ export class SpFFRTConfig extends BaseElement { Cmd.getProcess().then((processList: string[]): void => { selectInputEl.dataSource(processList, ''); if (processList.length > 0) { - processInputEl.readOnly = true; selectInputEl.dataSource(processList, 'ALL-Process'); } else { selectInputEl.dataSource([], ''); diff --git a/ide/src/trace/component/setting/SpHilogRecord.html.ts b/ide/src/trace/component/setting/SpHilogRecord.html.ts index d0454da906465c414f4ed2fa3949b04e529a37ca..a2f1c6cc890d3586e96c96ad82f78b44cac11184 100644 --- a/ide/src/trace/component/setting/SpHilogRecord.html.ts +++ b/ide/src/trace/component/setting/SpHilogRecord.html.ts @@ -92,9 +92,9 @@ export const SpHiLogRecordHtml = ` Process Record process
- - +
diff --git a/ide/src/trace/component/setting/SpHilogRecord.ts b/ide/src/trace/component/setting/SpHilogRecord.ts index 1c3f87c4b9b3c2cf0459b6016dcc1fe00e5d341c..628c23a20dd79771a547a6e77afaf47921f72f87 100644 --- a/ide/src/trace/component/setting/SpHilogRecord.ts +++ b/ide/src/trace/component/setting/SpHilogRecord.ts @@ -23,11 +23,12 @@ import { Cmd } from '../../../command/Cmd'; import { LitAllocationSelect } from '../../../base-ui/select/LitAllocationSelect'; import { LitSelect } from '../../../base-ui/select/LitSelect'; import { SpHiLogRecordHtml } from './SpHilogRecord.html'; +import { LitSelectV } from '../../../base-ui/select/LitSelectV'; @element('sp-hi-log') export class SpHilogRecord extends BaseElement { private vmTrackerSwitch: LitSwitch | undefined | null; - private processSelectEl: LitAllocationSelect | undefined | null; + private processSelectEl: LitSelectV | undefined | null; private logsSelectEl: LitSelect | undefined | null; get recordHilog(): boolean { @@ -47,7 +48,7 @@ export class SpHilogRecord extends BaseElement { initElements(): void { this.vmTrackerSwitch = this.shadowRoot?.querySelector('.hilog-switch') as LitSwitch; - this.processSelectEl = this.shadowRoot?.querySelector('.record-process-select') as LitAllocationSelect; + this.processSelectEl = this.shadowRoot?.querySelector('.record-process-select') as LitSelectV; this.logsSelectEl = this.shadowRoot?.querySelector('.record-logs-select') as LitSelect; let hiLogConfigList = this.shadowRoot?.querySelectorAll('.hilog-config-top'); this.vmTrackerSwitch.addEventListener('change', () => { @@ -62,20 +63,24 @@ export class SpHilogRecord extends BaseElement { }); } }); - let processInputEl = this.processSelectEl.shadowRoot?.querySelector('.multipleSelect') as HTMLInputElement; + let processInputEl = this.processSelectEl.shadowRoot?.querySelector('input') as HTMLInputElement; processInputEl.addEventListener('mousedown', () => { - if (SpRecordTrace.serialNumber === '') { - this.processSelectEl!.processData = []; - this.processSelectEl!.initData(); + if (this.recordHilog) { + if ((SpRecordTrace.serialNumber === '')) { + this.processSelectEl!.dataSource([], ''); + } else { + Cmd.getProcess().then((processList) => { + if (processList.length > 0) { + this.processSelectEl!.dataSource(processList, 'ALL-Process'); + } else { + this.processSelectEl!.dataSource([], ''); + } + }); + } + processInputEl!.removeAttribute('readonly'); } else { - Cmd.getProcess().then((processList) => { - if (processList.length > 0 && this.recordHilog) { - processInputEl!.setAttribute('readonly', 'readonly'); - } - processList.unshift('ALL-Process'); - this.processSelectEl!.processData = processList; - this.processSelectEl!.initData(); - }); + processInputEl!.setAttribute('readonly', 'readonly'); + return; } }); } diff --git a/ide/src/trace/component/setting/SpHisysEvent.html.ts b/ide/src/trace/component/setting/SpHisysEvent.html.ts index 6df54d17929014dc71edc203c367ffe2d13e0a60..0afe05d6f90e9d852023a850f6e058ca9097bdb3 100644 --- a/ide/src/trace/component/setting/SpHisysEvent.html.ts +++ b/ide/src/trace/component/setting/SpHisysEvent.html.ts @@ -88,17 +88,17 @@ export const SpHiSysEventHtml = ` +
+ +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + +`; 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 0000000000000000000000000000000000000000..661dd9ba36715abe93dbe5e814b238cd729e979b --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts @@ -0,0 +1,296 @@ +/* + * 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'; + +/// Hangs 框选Tab页1 +@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.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; + 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'; + }); + } + } + + refreshHangsTitle(): 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.isFilterHang(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.refreshHangsTitle(); + } + + 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 { + 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()); + this.sendEventTid = this.sendEventTid.split(':').at(-1)!; + } + + 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/TabPaneHangSummary.html.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.html.ts new file mode 100644 index 0000000000000000000000000000000000000000..1719855685994ffbf28a61833fc6372404b940eb --- /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 0000000000000000000000000000000000000000..4304bd25b82916a27b728ff750b14011ea607ddb --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts @@ -0,0 +1,285 @@ +/* + * 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'; + +/// 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; + + /// 框选时段范围时触发 + 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.buildTreeTbhNodes(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 buildTreeTbhNodes(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/config/custom_temp_config.json b/ide/src/trace/config/custom_temp_config.json index 012f77550e62d1e92555a563160742209988bace..af97ab92cb5034497d25f91272d8174466c64d58 100644 --- a/ide/src/trace/config/custom_temp_config.json +++ b/ide/src/trace/config/custom_temp_config.json @@ -1328,7 +1328,7 @@ "subsystem": "contacts_data", "components": [ { - "component": "contacts_data", + "component": "contacts_data_hap", "charts": [ { "chartName": "com.ohos.contacts", diff --git a/ide/src/trace/database/data-trafic/ClockDataReceiver.ts b/ide/src/trace/database/data-trafic/ClockDataReceiver.ts index 79dc5e013ea49d22613b5511deeba925a987b396..003775fb86bb8cf593fb595d2f0ae70472ee6df5 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,6 +164,7 @@ function arrayBufferHandler(data: unknown, res: unknown[], transfer: boolean): v // @ts-ignore value[i] = it.value; }); + (self as unknown as Worker).postMessage( { // @ts-ignore diff --git a/ide/src/trace/database/data-trafic/CommonArgs.ts b/ide/src/trace/database/data-trafic/CommonArgs.ts index 8994ae61a459be41b863da03d93e72b8e2e26753..ef61ea0a063abd4961a2325fa269ee19eff167ab 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 new file mode 100644 index 0000000000000000000000000000000000000000..10c5302700becb61d1fd3977dcf768d24d682e75 --- /dev/null +++ b/ide/src/trace/database/data-trafic/HangDataReceiver.ts @@ -0,0 +1,132 @@ +// 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 >= ${args.minDur} + AND c.name LIKE 'H:Et:%' + AND t.is_main_thread = 1 + AND p.pid = ${args.pid} +`.trim(); + +export interface HangSQLStruct { + id: number; + startNS: number; + dur: number; + tid: number; + pid: number; +} + +export function hangDataReceiver(data: unknown, proc: Function): void { + // @ts-ignore + if (data.params.trafic === TraficEnum.Memory) { + let res: HangSQLStruct[]; + let list: HangSQLStruct[]; + + // @ts-ignore + if (!hangList.has(data.params.pid)) { + // @ts-ignore + let sql = chartHangDataSql(data.params); + list = proc(sql); + // @ts-ignore + hangList.set(data.params.pid, list); + } + else { + // @ts-ignore + list = hangList.get(data.params.pid) || []; + } + + // @ts-ignore + 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 { + // @ts-ignore + let sql = chartHangDataSql(data.params); + let res: HangSQLStruct[] = proc(sql); + // @ts-ignore + arrayBufferHandler(data, res, data.params.trafic !== TraficEnum.SharedArrayBuffer); + } +} + +function arrayBufferHandler(data: unknown, res: HangSQLStruct[], transfer: boolean = true): void { + // @ts-ignore + let id = new Int32Array(transfer ? res.length : data.params.sharedArrayBuffers.id); + // @ts-ignore + let startNS = new Float64Array(transfer ? res.length : data.params.sharedArrayBuffers.startNS); + // @ts-ignore + let dur = new Float64Array(transfer ? res.length : data.params.sharedArrayBuffers.dur); + // @ts-ignore + let tid = new Int32Array(transfer ? res.length : data.params.sharedArrayBuffers.tid); + // @ts-ignore + 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 = { + // @ts-ignore + id: data.id, + // @ts-ignore + 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 0000000000000000000000000000000000000000..91ec3a2a42a7593338bbce7b5c2c91f42f79bc0d --- /dev/null +++ b/ide/src/trace/database/data-trafic/HangDataSender.ts @@ -0,0 +1,77 @@ +/* + * 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'; +import { FlagsConfig } from '../../component/SpFlags'; + +export function hangDataSender( + processId: number = 0, + row: TraceRow, + args?: unknown, +): Promise { + 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, + // @ts-ignore + queryAll: args && args.queryAll, + // @ts-ignore + selectStartNS: args ? args.startNS : 0, + // @ts-ignore + selectEndNS: args ? args.endNS : 0, + // @ts-ignore + 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: unknown, len: number): HangStruct[] { + let outArr: HangStruct[] = []; + // @ts-ignore + let id = new Int32Array(buffers.id); + // @ts-ignore + let startNS = new Float64Array(buffers.startNS); + // @ts-ignore + let dur = new Float64Array(buffers.dur); + // @ts-ignore + let tid = new Int32Array(buffers.tid); + // @ts-ignore + 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 d458819daefc55f8bc0c881d26b55fd97999a0c9..2088bc1522f95fce42e4d5e53a7a80fcd335bf0a 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 f39a460454dff88f5f4ebcb2544c9df5fe244d48..8ead725cc196d39c166415ed61bacb60ac4a5b6f 100644 --- a/ide/src/trace/database/data-trafic/utils/ExecProtoForWorker.ts +++ b/ide/src/trace/database/data-trafic/utils/ExecProtoForWorker.ts @@ -81,6 +81,7 @@ import { cpuFreqDataReceiver } from '../cpu/CpuFreqDataReceiver'; import { lostFrameReceiver } from './../LostFrameReceiver'; import { sliceReceiver, sliceSPTReceiver } from '../SliceReceiver'; import { dmaFenceReceiver } from './../dmaFenceReceiver'; +import { hangDataReceiver } from '../HangDataReceiver'; // @ts-ignore const traficHandlers: Map = new Map([]); // @ts-ignore @@ -103,6 +104,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 e51ccb00a62e912e2c1933ee85be3fcd908cd946..b24135acc755adc5ebec29eb8c0aa116054c7397 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 0000000000000000000000000000000000000000..35181e4b6176edbdb4135c5afced167148c1b322 --- /dev/null +++ b/ide/src/trace/database/sql/Hang.sql.ts @@ -0,0 +1,75 @@ +/* + * 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 { FlagsConfig } from '../../component/SpFlags'; +import { query } from '../SqlLite'; +import { HangStruct } from '../ui-worker/ProcedureWorkerHang'; + +function getMinDur(): string { + let flagsItemJson = JSON.parse(window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY)!); + let minDur = flagsItemJson.hangValue; + return minDur; +} + +export const queryHangData = (): Promise> => query( + 'queryHangData', + ` +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 t.is_main_thread = 1 + AND c.dur >= ${getMinDur()} +GROUP BY + p.pid +`.trim() +); + + +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 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/ProcedureWorker.ts b/ide/src/trace/database/ui-worker/ProcedureWorker.ts index 2116d04efc8773e245b41850fabedf2ff6dbccd8..0443c5b7336f0d634d8eaf2f4b196a0cc7566b95 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'; @@ -116,6 +117,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 0000000000000000000000000000000000000000..282af5f10d4a698a56130aee1e6505ab3311e7be --- /dev/null +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts @@ -0,0 +1,139 @@ +/* + * 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, 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; + }, + 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: 2, + 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(); + } +} + +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()); + } else { + 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; + // 手动补充 在tab页中需要手动解析内容 + content: string | undefined; + + static getFrameColor(data: HangStruct): string { + return ({ + 'Instant': '#559CFF', + 'Circumstantial': '#FFE44D', + 'Micro': '#FEB354', + 'Severe': '#FC7470', + '': '', + })[data.type!]; + } + + static draw(ctx: CanvasRenderingContext2D, data: HangStruct): void { + if (data.frame) { + ctx.fillStyle = HangStruct.getFrameColor(data); + ctx.strokeStyle = HangStruct.getFrameColor(data); + + 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 (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; + } + } + + static isHover(data: HangStruct): boolean { + return data === HangStruct.hoverHangStruct || data === HangStruct.selectHangStruct; + } +} diff --git a/ide/test/hdc/hdcclient/HdcClient.test.ts b/ide/test/hdc/hdcclient/HdcClient.test.ts index b1cc5e74ad4b3906cdba3c66bc2bfd4a908dc37b..29409282917993bfe8a1be4ab706ef2a7f0bb031 100644 --- a/ide/test/hdc/hdcclient/HdcClient.test.ts +++ b/ide/test/hdc/hdcclient/HdcClient.test.ts @@ -40,4 +40,7 @@ describe('HdcClient Test', () => { }; expect(hdcClient.createDataMessage(data)).toBeUndefined(); }); + it('HdcClientTest07', function () { + expect(hdcClient.bindStream()).toBeUndefined(); + }); }); diff --git a/ide/test/statistics/util/SpStatisticsHttpUtil.test.ts b/ide/test/statistics/util/SpStatisticsHttpUtil.test.ts index e743e6b2816b8ad82b8e14bd33981655a2b9a4d5..20f68f6272d607fcf07ce0a7f27a7c45074d69a3 100644 --- a/ide/test/statistics/util/SpStatisticsHttpUtil.test.ts +++ b/ide/test/statistics/util/SpStatisticsHttpUtil.test.ts @@ -20,6 +20,8 @@ describe('SpStatisticsHttpUtil Test', () => { let mockXMLHttpRequest; let originalFetch; let mockFetch; + let originalXMLHttp; + let mockXMLHttp; beforeAll(() => { // Mock XMLHttpRequest @@ -34,7 +36,6 @@ describe('SpStatisticsHttpUtil Test', () => { } }, })); - global.XMLHttpRequest = mockXMLHttpRequest; // Mock fetch originalFetch = global.fetch; @@ -55,6 +56,7 @@ describe('SpStatisticsHttpUtil Test', () => { afterAll(() => { global.XMLHttpRequest = originalXMLHttpRequest; global.fetch = originalFetch; + global.XMLHttp = originalXMLHttp }); afterEach(() => { mockXMLHttpRequest.mockClear(); diff --git a/ide/test/trace/database/ui-worker/ProcedureWorker.test.ts b/ide/test/trace/database/ui-worker/ProcedureWorker.test.ts index fcc344cb06ee6f2f9af7a0752cbcab49e4d05fff..cee11acc6b04e3ec123743812aef4e94ab1d8d0d 100644 --- a/ide/test/trace/database/ui-worker/ProcedureWorker.test.ts +++ b/ide/test/trace/database/ui-worker/ProcedureWorker.test.ts @@ -21,6 +21,16 @@ describe('ProcedureWorker Test', () => { globalAlpha: 0.5, fillStyle: '#666666', fillRect: '', + }; + const data = { + startX: '', + endX: '', + startNS: '', + endNS: '', + x: '', + y: '', + height: 1, + width: 1, }; const params = { isRangeSelect: {}, diff --git a/trace_streamer/src/base/string_help.cpp b/trace_streamer/src/base/string_help.cpp index e3c7877ec58e25511aa70432d596615b4de8c9e2..f18316d33e958b1df1f235b6fbaa8ae681501925 100644 --- a/trace_streamer/src/base/string_help.cpp +++ b/trace_streamer/src/base/string_help.cpp @@ -13,6 +13,8 @@ * limitations under the License. */ #include "string_help.h" +#include +#include namespace SysTuning { namespace base { char *GetDemangleSymbolIndex(const char *mangled) @@ -116,7 +118,7 @@ std::string StrTrim(const std::string &input) } // in-place版本, 直接修改str -void StrTrim(std::string& input) +void StrTrim(std::string &input) { if (input.empty()) { return; @@ -133,5 +135,23 @@ void RemoveNullTerminator(std::string &str) pos = str.rfind('\0'); } } + +uint32_t StrHash(const std::string &str, uint32_t maxValue) +{ + if (maxValue == 0) { + return 0; + } + static const uint32_t COLOR_A = 9876023; + static const uint32_t COLOR_B = 0xffffffff; + uint32_t hash = 0x11c9dc5; + for (const auto &chr : str) { + if (std::isdigit(chr)) { + continue; + } + hash ^= chr; + hash = (hash * COLOR_A) & COLOR_B; + } + return hash % maxValue; +} } // namespace base } // namespace SysTuning diff --git a/trace_streamer/src/base/string_help.h b/trace_streamer/src/base/string_help.h index 476f30bee6dcfe7665b66b6dcf15bd9f0d6353f6..c104641078c1baae30e38c2e020c1d2f0a43a81d 100644 --- a/trace_streamer/src/base/string_help.h +++ b/trace_streamer/src/base/string_help.h @@ -29,9 +29,10 @@ bool EndWith(const std::string &str, const std::string &res); std::string FormatString(const char *p); std::string Strip(const std::string &str); std::string StrTrim(const std::string &input); -void StrTrim(std::string& input); +void StrTrim(std::string &input); std::string TrimInvisibleCharacters(const std::string &str); void RemoveNullTerminator(std::string &str); +uint32_t StrHash(const std::string &str, uint32_t maxValue = 20); } // namespace base } // namespace SysTuning #endif // SRC_TRACE_BASE_STRINGHELP_H diff --git a/trace_streamer/src/table/ftrace/callstack_table.cpp b/trace_streamer/src/table/ftrace/callstack_table.cpp index 1f3773752df4b95e5ce2d35a043c6cdef001557b..48a1bda1f33c5763ee9b01fa60a6e0f84a9f4db1 100644 --- a/trace_streamer/src/table/ftrace/callstack_table.cpp +++ b/trace_streamer/src/table/ftrace/callstack_table.cpp @@ -26,6 +26,9 @@ enum class Index : int32_t { NAME, DEPTHS, COOKIES_ID, +#if IS_WASM + COLORINDEX, +#endif PARENT_ID, ARGSET, CHAIN_IDS, @@ -43,6 +46,9 @@ CallStackTable::CallStackTable(const TraceDataCache *dataCache) : TableBase(data tableColumn_.push_back(TableBase::ColumnInfo("name", "TEXT")); tableColumn_.push_back(TableBase::ColumnInfo("depth", "INTEGER")); tableColumn_.push_back(TableBase::ColumnInfo("cookie", "INTEGER")); +#if IS_WASM + tableColumn_.push_back(TableBase::ColumnInfo("colorIndex", "INTEGER")); +#endif tableColumn_.push_back(TableBase::ColumnInfo("parent_id", "INTEGER")); tableColumn_.push_back(TableBase::ColumnInfo("argsetid", "INTEGER")); tableColumn_.push_back(TableBase::ColumnInfo("chainId", "TEXT")); @@ -157,6 +163,11 @@ int32_t CallStackTable::Cursor::Column(int32_t col) const case Index::CALL_IDS: SetTypeColumnInt64(slicesObj_.CallIds()[CurrentRow()], INVALID_UINT64); break; +#if IS_WASM + case Index::COLORINDEX: + SetTypeColumnInt64(slicesObj_.ColorIndexs()[CurrentRow()], INVALID_UINT64); + break; +#endif case Index::CATS: { SetTypeColumnText(slicesObj_.CatsData()[CurrentRow()], INVALID_UINT64); break; diff --git a/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.cpp b/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.cpp index 47fd67ca978b4f3dddcf3e6165e7adbd0bff8676..b13386b5a37b963ba4dd9e13be4e494031c7c72d 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.cpp +++ b/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.cpp @@ -43,6 +43,7 @@ void CallStack::AppendCommonInfo(uint64_t startT, uint64_t durationNs, InternalT timeStamps_.emplace_back(startT); durs_.emplace_back(durationNs); callIds_.emplace_back(internalTid); + colorIndexs_.emplace_back(0); } void CallStack::AppendCallStack(DataIndex cat, DataIndex name, uint8_t depth, std::optional parentId) { @@ -108,6 +109,10 @@ void CallStack::SetArgSetId(size_t index, uint32_t argSetId) { argSet_[index] = argSetId; } +void CallStack::SetColorIndex(size_t index, uint32_t colorIndex) +{ + colorIndexs_[index] = colorIndex; +} const std::deque> &CallStack::ParentIdData() const { return parentIds_; @@ -132,6 +137,10 @@ const std::deque &CallStack::CallIds() const { return callIds_; } +const std::deque &CallStack::ColorIndexs() const +{ + return colorIndexs_; +} const std::deque &CallStack::ChainIds() const { return chainIds_; diff --git a/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.h b/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.h index c0736c0c3e8761f97c9e2846200aa0801f03bdd7..033d25db69434ed63d6e59f31b1214569fbe219e 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.h +++ b/trace_streamer/src/trace_data/trace_stdtype/ftrace/callstack_stdtype.h @@ -15,6 +15,8 @@ #ifndef CALLSTACK_STDTYPE_H #define CALLSTACK_STDTYPE_H +#include +#include #include #include "base_stdtype.h" @@ -49,6 +51,7 @@ public: void SetTimeStamp(size_t index, uint64_t timeStamp); void SetDepth(size_t index, uint8_t depth); void SetArgSetId(size_t index, uint32_t argSetId); + void SetColorIndex(size_t index, uint32_t colorIndex); void Clear() override { CacheBase::Clear(); @@ -56,6 +59,7 @@ public: cats_.clear(); cookies_.clear(); callIds_.clear(); + colorIndexs_.clear(); names_.clear(); depths_.clear(); chainIds_.clear(); @@ -66,8 +70,8 @@ public: } void ClearExportedData() override { - EraseElements(timeStamps_, ids_, durs_, cats_, cookies_, callIds_, names_, depths_, chainIds_, spanIds_, - parentSpanIds_, flags_, argSet_); + EraseElements(timeStamps_, ids_, durs_, cats_, cookies_, colorIndexs_, callIds_, names_, depths_, chainIds_, + spanIds_, parentSpanIds_, flags_, argSet_); } const std::deque> &ParentIdData() const; const std::deque &CatsData() const; @@ -75,6 +79,7 @@ public: const std::deque &Depths() const; const std::deque &Cookies() const; const std::deque &CallIds() const; + const std::deque &ColorIndexs() const; const std::deque &ChainIds() const; const std::deque &SpanIds() const; const std::deque &ParentSpanIds() const; @@ -92,7 +97,7 @@ private: std::deque callIds_ = {}; std::deque names_ = {}; std::deque depths_ = {}; - + std::deque colorIndexs_ = {}; std::deque chainIds_ = {}; std::deque spanIds_ = {}; std::deque parentSpanIds_ = {}; diff --git a/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp b/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp index d747e9682a1a9a39132889685a59e1f3a8dfc68e..ee33b9f588ff40a044def33b1f02b61c33890606 100644 --- a/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp +++ b/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp @@ -213,6 +213,9 @@ void TraceStreamerSelector::WaitForParserEnd() streamFilters_->animationFilter_->UpdateFrameInfo(); streamFilters_->animationFilter_->UpdateDynamicFrameInfo(); } +#if IS_WASM + ComputeDataDictStrHash(); +#endif } MetaData *TraceStreamerSelector::GetMetaData() @@ -579,5 +582,15 @@ bool TraceStreamerSelector::ParserAndPrintMetrics(const std::string &metrics) } return true; } + +void TraceStreamerSelector::ComputeDataDictStrHash() +{ + auto callStack = traceDataCache_->GetConstInternalSlicesData(); + for (auto i = 0; i < callStack.NamesData().size(); i++) { + auto str = traceDataCache_->GetDataFromDict(callStack.NamesData()[i]); + auto res = StrHash(str); + traceDataCache_->GetInternalSlicesData()->SetColorIndex(i, res); + } +} } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/trace_streamer/trace_streamer_selector.h b/trace_streamer/src/trace_streamer/trace_streamer_selector.h index e00974571475caf790ff211978fca33f0539718a..c4d9bdeefecabe007e0fa032bd368e7fe2b5a627 100644 --- a/trace_streamer/src/trace_streamer/trace_streamer_selector.h +++ b/trace_streamer/src/trace_streamer/trace_streamer_selector.h @@ -115,6 +115,7 @@ public: private: void InitFilter(); bool LoadQueryFile(const std::string &sqlOperator, std::vector &sqlStrings); + void ComputeDataDictStrHash(); TraceFileType fileType_; std::unique_ptr streamFilters_ = {}; std::unique_ptr traceDataCache_ = {}; diff --git a/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp b/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp index 782449d8148eaff6423832b06be69fc1bf6c05c0..e5fd8939d1f206bf059bff4715b07bef96fa51f2 100644 --- a/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp +++ b/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp @@ -95,7 +95,7 @@ public: */ HWTEST_F(EbpfBioParserTest, EbpfBioParserCorrectWithoutCallback, TestSize.Level1) { - TS_LOGI("test32-1"); + TS_LOGI("test32-01"); InitData(sizeof(BIOFixedHeader), 0, START_TIME, END_TIME); std::unique_ptr ebpfDataParser = @@ -126,7 +126,7 @@ HWTEST_F(EbpfBioParserTest, EbpfBioParserCorrectWithoutCallback, TestSize.Level1 */ HWTEST_F(EbpfBioParserTest, EbpfBioParserwrongWithoutCallback, TestSize.Level1) { - TS_LOGI("test32-2"); + TS_LOGI("test32-02"); InitData(sizeof(BIOFixedHeader), 0, END_TIME, START_TIME, 1); std::unique_ptr ebpfDataParser = @@ -159,7 +159,7 @@ HWTEST_F(EbpfBioParserTest, EbpfBioParserwrongWithoutCallback, TestSize.Level1) */ HWTEST_F(EbpfBioParserTest, EbpfBioParserCorrectWithOneCallback, TestSize.Level1) { - TS_LOGI("test32-3"); + TS_LOGI("test32-03"); InitData(sizeof(BIOFixedHeader), 1, START_TIME, END_TIME); const uint64_t ips[1] = {IPS_01}; @@ -198,7 +198,7 @@ HWTEST_F(EbpfBioParserTest, EbpfBioParserCorrectWithOneCallback, TestSize.Level1 */ HWTEST_F(EbpfBioParserTest, EbpfBioParserCorrectWithMultipleCallback, TestSize.Level1) { - TS_LOGI("test32-4"); + TS_LOGI("test32-04"); InitData(sizeof(BIOFixedHeader) + 2 * sizeof(uint64_t), 2, START_TIME, END_TIME); const uint64_t ips[2] = {IPS_01, IPS_02}; diff --git a/trace_streamer/test/unittest/filter/animation_filter_test.cpp b/trace_streamer/test/unittest/filter/animation_filter_test.cpp index 085407b650a874b07be8773e005b21f3803b2f4b..ed3f3fb719f9314276373d792dc669d33f3fb8d1 100644 --- a/trace_streamer/test/unittest/filter/animation_filter_test.cpp +++ b/trace_streamer/test/unittest/filter/animation_filter_test.cpp @@ -76,6 +76,7 @@ HWTEST_F(AnimationFilterTest, InvalidCallStack, TestSize.Level1) TS_LOGI("test36-2"); TracePoint point; std::string validName("H:RSUniRender::Process:[WindowScene_xxx] (0, 0, 1344, 2772) Alpha: 1.00"); + std::string newValidName("H:RSSurfaceRenderNodeDrawable::OnDraw:[WindowScene_xxx] (0, 0, 1344, 2772) Alpha: 1.00"); std::string invalidName("H:RSUniRender::Process:[xxx] (0, 0, 1344, 2772) Alpha: 1.00"); const size_t CALLSTACK_SLICE_ID = 1; CallStack *callStackSlice = stream_.traceDataCache_->GetInternalSlicesData(); @@ -83,10 +84,12 @@ HWTEST_F(AnimationFilterTest, InvalidCallStack, TestSize.Level1) stream_.traceDataCache_->GetDataIndex("H:RSMainThread::DoComposition"), stream_.traceDataCache_->GetDataIndex("H:ProcessDisplayRenderNode[0](0,0,0,0)"), stream_.traceDataCache_->GetDataIndex(validName), + stream_.traceDataCache_->GetDataIndex(newValidName), stream_.traceDataCache_->GetDataIndex(invalidName), }; std::vector funcPrefixs{ "H:RSUniRender::Process:[WindowScene_xxx]", + "H:RSSurfaceRenderNodeDrawable::OnDraw:[WindowScene_xxx]", "H:RSUniRender::Process:[xxx]", }; // invalid parentId @@ -111,7 +114,7 @@ HWTEST_F(AnimationFilterTest, InvalidCallStack, TestSize.Level1) INVALID_UINT64, callStackNames[i], depth}; index = callStackSlice->AppendInternalSlice(callStackInternalRow1, parentId); } - point.funcPrefix_ = funcPrefixs[1]; + point.funcPrefix_ = funcPrefixs[2]; point.name_ = invalidName; auto curStackRow = 1; auto res = stream_.streamFilters_->animationFilter_->BeginDynamicFrameEvent(point, curStackRow); @@ -122,6 +125,11 @@ HWTEST_F(AnimationFilterTest, InvalidCallStack, TestSize.Level1) curStackRow = 2; res = stream_.streamFilters_->animationFilter_->BeginDynamicFrameEvent(point, curStackRow); EXPECT_TRUE(res); + point.funcPrefix_ = funcPrefixs[1]; + point.name_ = newValidName; + curStackRow = 3; + res = stream_.streamFilters_->animationFilter_->BeginDynamicFrameEvent(point, curStackRow); + EXPECT_TRUE(res); } /** @@ -228,6 +236,7 @@ HWTEST_F(AnimationFilterTest, UpdateDynamicFrameInfo, TestSize.Level1) stream_.traceDataCache_->GetDataIndex("H:ProcessDisplayRenderNode[0](0,0,0,0)"), stream_.traceDataCache_->GetDataIndex( "H:RSUniRender::Process:[WindowScene_xxx] (0, 0, 1344, 2772) Alpha: 1.00"), + }; std::string funcPrefix("H:RSUniRender::Process:[WindowScene_xxx]"); uint64_t index = INVALID_UINT64; diff --git a/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp b/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp index 3f3f8a62341a5a7d5a838e0dd6224c5cd80b7880..8875eb206300e94c0c12992b74356e385818eee0 100644 --- a/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp +++ b/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp @@ -66,8 +66,6 @@ HWTEST_F(EventParserTest, ParseLine, TestSize.Level1) bytraceLine.pid = 1; bytraceLine.cpu = 0; bytraceLine.task = "ACCS0-2716"; - bytraceLine.pidStr = "12"; - bytraceLine.tGidStr = "12"; bytraceLine.eventName = "sched_switch"; bytraceLine.argsStr = "prev_comm=ACCS0 prev_pid=2716 prev_prio=120 \ @@ -98,8 +96,6 @@ HWTEST_F(EventParserTest, ParseLineNotEnoughArgs, TestSize.Level1) bytraceLine.pid = 1; bytraceLine.cpu = 0; bytraceLine.task = "ACCS0-2716"; - bytraceLine.pidStr = "12"; - bytraceLine.tGidStr = "12"; bytraceLine.eventName = "sched_switch"; bytraceLine.argsStr = "prev_state=R ==> next_comm=kworker/0:0 next_pid=8326 next_prio=120"; BytraceEventParser eventParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); @@ -121,8 +117,6 @@ HWTEST_F(EventParserTest, ParseLineUnCognizableEventname, TestSize.Level1) bytraceLine.pid = 1; bytraceLine.cpu = 0; bytraceLine.task = "ACCS0-2716"; - bytraceLine.pidStr = "12"; - bytraceLine.tGidStr = "12"; bytraceLine.eventName = "ThisEventNameDoNotExist"; // UnRecognizable event name bytraceLine.argsStr = "prev_comm=ACCS0 prev_pid=2716 prev_prio=120 \ @@ -146,9 +140,10 @@ HWTEST_F(EventParserTest, ParseSchedSwitchNoArgs, TestSize.Level1) bytraceLine.pid = 1; bytraceLine.cpu = 0; bytraceLine.task = "ACCS0-2716"; - bytraceLine.pidStr = "12"; - bytraceLine.tGidStr = "12"; bytraceLine.eventName = "sched_switch"; + bytraceLine.argsStr = + "prev_comm=ACCS0 prev_pid=2716 prev_prio=120 \ + prev_state=R ==> next_comm=kworker/0:0 next_pid=8326 next_prio=120"; BytraceEventParser eventParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); eventParser.ParseDataItem(bytraceLine); eventParser.FilterAllEvents(); diff --git a/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp b/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp index 1d85bd1f98e5f88c8a61b87b0e03ce66c3fa241a..8305d395470a681fd1b7a0ea925008f504e62977 100644 --- a/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp +++ b/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp @@ -190,8 +190,8 @@ HWTEST_F(PtreaderParserTest, LineParser_abnormal_pid_err, TestSize.Level1) { TS_LOGI("test2-7"); std::string str( - "ACCS0-27X6 ( 2519) [000] ...1 168758.662861: binder_transaction: \ - transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n"); + "ACCS0-2716 ( 2519) [000] ...1 168758.662861: binder_transaction: \ + transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=2716 reply=0 flags=0x10 code=0x3 \n"); PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); ptreaderParser.ParseTraceDataItem(str); ptreaderParser.WaitForParserEnd(); @@ -211,7 +211,7 @@ HWTEST_F(PtreaderParserTest, LineParserWithInvalidCpu, TestSize.Level1) TS_LOGI("test2-8"); std::string str( "ACCS0-2716 ( 2519) [00X] ...1 168758.662861: binder_transaction: \ - transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n"); + transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=2716 reply=0 flags=0x10 code=0x3 \n"); PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); ptreaderParser.ParseTraceDataItem(str); ptreaderParser.WaitForParserEnd(); @@ -231,7 +231,7 @@ HWTEST_F(PtreaderParserTest, LineParserWithInvalidTs, TestSize.Level1) TS_LOGI("test2-9"); std::string str( "ACCS0-2716 ( 2519) [000] ...1 168758.662X61: binder_transaction: \ - transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n"); + transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=2716 reply=0 flags=0x10 code=0x3 \n"); PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); ptreaderParser.ParseTraceDataItem(str); ptreaderParser.WaitForParserEnd(); diff --git a/trace_streamer/test/unittest/table/table_test.cpp b/trace_streamer/test/unittest/table/table_test.cpp index 07fd1f85ec98d5fe386ebf69fcc7c5278477e331..130edca9fbd1b2b26e4dfca01f7265495be94686 100644 --- a/trace_streamer/test/unittest/table/table_test.cpp +++ b/trace_streamer/test/unittest/table/table_test.cpp @@ -198,6 +198,7 @@ HWTEST_F(TableTest, ClockEventFilterTableTest, TestSize.Level1) std::string sqlSelect3 = "select * from clock_event_filter where name < 1"; std::string sqlSelect4 = "select * from clock_event_filter where cpu >= 1"; std::string sqlSelect5 = "select * from clock_event_filter where id <= 1"; + std::string sqlSelect6 = "select * from clock_event_filter where index = 1"; uint64_t id = 1; uint64_t type = 1; DataIndex name = stream_.traceDataCache_->GetDataIndex("name"); @@ -230,6 +231,7 @@ HWTEST_F(TableTest, CpuMeasureFilterTableTest, TestSize.Level1) std::string sqlSelect3 = "select * from cpu_measure_filter where type < 1"; std::string sqlSelect4 = "select * from cpu_measure_filter where name >= 1"; std::string sqlSelect5 = "select * from cpu_measure_filter where cpu <= 1"; + std::string sqlSelect6 = "select * from cpu_measure_filter where index = 1"; uint64_t filterId = 2; DataIndex name = stream_.traceDataCache_->GetDataIndex("name"); uint32_t cpu = 1;