diff --git a/ide/src/base-ui/chart/column/LitChartColumn.ts b/ide/src/base-ui/chart/column/LitChartColumn.ts index 818412eba75ef6162c7f701b610d2d7bedd429e9..19ddfa533dec79e87c2247b4be09cc9817dd3e9b 100644 --- a/ide/src/base-ui/chart/column/LitChartColumn.ts +++ b/ide/src/base-ui/chart/column/LitChartColumn.ts @@ -207,40 +207,42 @@ export class LitChartColumn extends BaseElement { for (let i = 0; i <= 5; i++) { this.rowLines.push({ y: gap * i, - label: `${getProbablyTime(maxValue - valGap * i)}`, + label: this.litChartColumnCfg.removeUnit === true ? `${maxValue - valGap * i}` : `${getProbablyTime(maxValue - valGap * i)}`, }); } - this.litChartColumnCfg?.data - .sort((a, b) => b[this.litChartColumnCfg!.yField] - a[this.litChartColumnCfg!.yField]) - .forEach((litChartColumnItem, litChartColumnIndex, array) => { - this.data.push({ - color: this.litChartColumnCfg!.color(litChartColumnItem), - obj: litChartColumnItem, - root: true, - xLabel: litChartColumnItem[this.litChartColumnCfg!.xField], - yLabel: litChartColumnItem[this.litChartColumnCfg!.yField], - bgFrame: { - x: this.offset!.x! + partWidth * litChartColumnIndex, - y: 0, - w: partWidth, - h: partHeight, - }, - centerX: this.offset!.x! + partWidth * litChartColumnIndex + partWidth / 2, - centerY: - partHeight - - (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue + - (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue / 2, - frame: { - x: this.offset!.x! + partWidth * litChartColumnIndex + partWidth / 6, - y: partHeight - (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue, - w: partWidth - partWidth / 3, - h: (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue, - }, - height: 0, - heightStep: Math.ceil((litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue / 60), - process: true, - }); + if (!this.litChartColumnCfg.notSort) { + this.litChartColumnCfg?.data + .sort((a, b) => b[this.litChartColumnCfg!.yField] - a[this.litChartColumnCfg!.yField]); + } + this.litChartColumnCfg?.data.forEach((litChartColumnItem, litChartColumnIndex, array) => { + this.data.push({ + color: this.litChartColumnCfg!.color(litChartColumnItem), + obj: litChartColumnItem, + root: true, + xLabel: litChartColumnItem[this.litChartColumnCfg!.xField], + yLabel: litChartColumnItem[this.litChartColumnCfg!.yField], + bgFrame: { + x: this.offset!.x! + partWidth * litChartColumnIndex, + y: 0, + w: partWidth, + h: partHeight, + }, + centerX: this.offset!.x! + partWidth * litChartColumnIndex + partWidth / 2, + centerY: + partHeight - + (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue + + (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue / 2, + frame: { + x: this.offset!.x! + partWidth * litChartColumnIndex + partWidth / 6, + y: partHeight - (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue, + w: partWidth - partWidth / 3, + h: (litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue, + }, + height: 0, + heightStep: Math.ceil((litChartColumnItem[this.litChartColumnCfg!.yField] * partHeight) / maxValue / 60), + process: true, }); + }); } else { let reduceGroup = this.litChartColumnCfg.data.reduce((pre, current, index, arr) => { (pre[current[this.litChartColumnCfg!.xField]] = pre[current[this.litChartColumnCfg!.xField]] || []).push( @@ -259,7 +261,7 @@ export class LitChartColumn extends BaseElement { for (let index = 0; index <= 5; index++) { this.rowLines.push({ y: gap * index, - label: `${getProbablyTime(maxValue - valGap * index)} `, + label: `${getProbablyTime(maxValue - valGap * index)}`, }); } Reflect.ownKeys(reduceGroup) diff --git a/ide/src/base-ui/chart/column/LitChartColumnConfig.ts b/ide/src/base-ui/chart/column/LitChartColumnConfig.ts index b508e8278349182ee2f1a533a1d24374657c32fd..0b3283a39fc36344fca5748856b757ac72e623fe 100644 --- a/ide/src/base-ui/chart/column/LitChartColumnConfig.ts +++ b/ide/src/base-ui/chart/column/LitChartColumnConfig.ts @@ -19,14 +19,16 @@ export interface LitChartColumnConfig { xField: string; yField: string; seriesField: string; + notSort?: boolean; + removeUnit?: boolean; color: (a: any) => string; tip: ((a: any) => string) | undefined; hoverHandler?: (no: number) => void; label: - | { - offset: number; - content: (it: any) => string; - } - | undefined - | null; + | { + offset: number; + content: (it: any) => string; + } + | undefined + | null; } diff --git a/ide/src/trace/bean/BinderProcessThread.ts b/ide/src/trace/bean/BinderProcessThread.ts new file mode 100644 index 0000000000000000000000000000000000000000..71acc0433e3b008dcbe3e3121be9c5d8029cb8a8 --- /dev/null +++ b/ide/src/trace/bean/BinderProcessThread.ts @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class BinderGroup { + title: string | null | undefined = ''; + count: number = 0; + totalCount: number = 0; + binderAsyncRcvCount?: number = 0; + binderReplyCount?: number = 0; + binderTransactionAsyncCount?: number = 0; + binderTransactionCount?: number = 0; + tid: number = 0; + pid: number = 0; + thread: string = ''; + process: string = ''; + name: string = ''; + cycleStartTime: number = 0; + cycleDur: number = 0; + id: number = 0; + children?: Array; + type?: string = ''; + status?: boolean = false; + idx: number = 0; +} + +export class DataSource { + xName: string = ''; + yAverage: number = 0; +} + +export class FuncNameCycle { + funcName: string = ''; + cycleStartTime: number = 0; + cycleDur: number = 0; + startTime: number = 0; + endTime: number = 0; + id: number = 0; + tid: number = 0; + pid: number = 0; +} + +export class BinderColumn { + name: string = ''; + count: number = 0; + dur: number = 0; + startNS: number = 0; + idx: number = 0 +} \ No newline at end of file diff --git a/ide/src/trace/component/SpHelp.ts b/ide/src/trace/component/SpHelp.ts index d0e5d80c0551cee47aecb26cc2c171631b7ee69d..d8ca953ab1cc975783f8b41ed1e79cb0826d591a 100644 --- a/ide/src/trace/component/SpHelp.ts +++ b/ide/src/trace/component/SpHelp.ts @@ -127,7 +127,6 @@ export class SpHelp extends BaseElement { title: 'Native Memory抓取和展示说明', icon: '', clickHandler: function (item: MenuItem) { - console.log('22222'); SpStatisticsHttpUtil.addOrdinaryVisitAction({ event: 'native', action: 'help_doc', @@ -391,7 +390,6 @@ export class SpHelp extends BaseElement { title: 'Ability Monitor抓取和展示说明', icon: '', clickHandler: function (item: MenuItem) { - console.log('444444'); SpStatisticsHttpUtil.addOrdinaryVisitAction({ event: 'ability', action: 'help_doc', diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index cbdd17a343fc330e8478dcfeee298b8b316d3320..d2941ceb361c53dd913b8f77b9ff5b136a01357d 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -108,6 +108,7 @@ import { type HiSysEventStruct } from '../database/ui-worker/ProcedureWorkerHiSy import { InitAnalysis } from '../database/logic-worker/ProcedureLogicWorkerCommon.js'; import { type SpKeyboard } from '../component/SpKeyboard.js'; import { drawVSync, enableVSync, setVSyncDisable } from './chart/VSync.js'; +import { binderStruct } from '../database/ui-worker/procedureWorkerBinder.js'; function dpr() { return window.devicePixelRatio || 1; @@ -4695,12 +4696,15 @@ export class SpSystemTrace extends BaseElement { } if (this.tipEL) { this.tipEL.innerHTML = html; - if (row.rowType === TraceRow.ROW_TYPE_JS_CPU_PROFILER || row.rowType === TraceRow.ROW_TYPE_PERF_CALLCHART) { + if (row.rowType === TraceRow.ROW_TYPE_JS_CPU_PROFILER || row.rowType === TraceRow.ROW_TYPE_PERF_CALLCHART || row.rowType === TraceRow.ROW_TYPE_BINDER_COUNT) { this.tipEL.style.maxWidth = row.clientWidth / 3 + 'px'; this.tipEL.style.wordBreak = ' break-all'; this.tipEL.style.height = 'unset'; this.tipEL.style.display = 'block'; y = y + struct.depth * 20; + if (row.rowType === TraceRow.ROW_TYPE_BINDER_COUNT) { + y = row.hoverY + row.getBoundingClientRect().top - this.getBoundingClientRect().top; + } } else { this.tipEL.style.display = 'flex'; this.tipEL.style.height = row.style.height; diff --git a/ide/src/trace/component/chart/SpSegmentationChart.ts b/ide/src/trace/component/chart/SpSegmentationChart.ts index d8ee149602d62652c35d08684abe1cb9988b8c0f..443ef90ac5a0d8a3f6a12363cc4be91ad06d74ab 100644 --- a/ide/src/trace/component/chart/SpSegmentationChart.ts +++ b/ide/src/trace/component/chart/SpSegmentationChart.ts @@ -100,17 +100,17 @@ export class SegMenTaTion { SegMenTaTion.schedRow!.supplier = (): Promise> => new Promise>((resolve) => resolve(chartData)); } else if (type === 'BINDER') { + binderStruct.maxHeight = 0; let binderList: any = []; let chartData: any; data.map((v: any) => { - let listCount = 0 + let listCount = 0; v.map((t: any) => { listCount += t.count if (t.name === 'binder transaction') { t.depth = t.count } if (t.name === 'binder transaction async') { - console.log(t, 'ttttttttt') t.depth = t.count + ((v.filter((i: any) => { return i.name === 'binder transaction' }).length > 0) ? (v.filter((i: any) => { @@ -186,6 +186,7 @@ export class SegMenTaTion { CpuFreqExtendStruct.hoverCpuFreqStruct = undefined } } else if (type === 'BINDER') { + binderStruct.isTableHover = tableIsHover; if (tableIsHover) { binderStruct.hoverCycle = cycle } else { @@ -216,7 +217,7 @@ export class SegMenTaTion { async initFolder() { let row = TraceRow.skeleton(); row.setAttribute('disabled-check', ''); - row.rowId = `unkown`; + row.rowId = `segmentation`; row.index = 0; row.rowType = TraceRow.ROW_TYPE_SEGMENTATION; row.rowParentId = ''; @@ -227,7 +228,7 @@ export class SegMenTaTion { row.onThreadHandler = (useCache) => { row.canvasSave(SegMenTaTion.trace.canvasPanelCtx!); if (row.expansion) { - SegMenTaTion.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height); + SegMenTaTion.trace.canvasPanelCtx!.clearRect(0, 0, row.frame.width, row.frame.height); } else { (renders['empty'] as EmptyRender).renderMainThread( { @@ -241,7 +242,7 @@ export class SegMenTaTion { row.canvasRestore(SegMenTaTion.trace.canvasPanelCtx!); }; this.rowFolder = row; - SegMenTaTion.trace.rowsEL?.appendChild(row); + SegMenTaTion.trace.rowsEL!.appendChild(row); } @@ -268,7 +269,7 @@ export class SegMenTaTion { }) } SegMenTaTion.jsonRow.focusHandler = (ev) => { - SegMenTaTion.trace?.displayTip( + SegMenTaTion.trace!.displayTip( SegMenTaTion.jsonRow!, CpuFreqExtendStruct.hoverCpuFreqStruct, `${ColorUtils.formatNumberComma(CpuFreqExtendStruct.hoverCpuFreqStruct === undefined ? 0 : CpuFreqExtendStruct.hoverCpuFreqStruct.value! || 0)}` @@ -295,7 +296,7 @@ export class SegMenTaTion { ); SegMenTaTion.jsonRow!.canvasRestore(context); }; - SegMenTaTion.trace.rowsEL?.appendChild(SegMenTaTion.jsonRow); + SegMenTaTion.trace.rowsEL!.appendChild(SegMenTaTion.jsonRow); this.rowFolder!.addChildTraceRow(SegMenTaTion.jsonRow); } @@ -311,7 +312,7 @@ export class SegMenTaTion { SegMenTaTion.GpuRow.supplier = (): Promise> => new Promise>((resolve) => resolve([])); SegMenTaTion.GpuRow.focusHandler = (ev) => { - SegMenTaTion.trace?.displayTip( + SegMenTaTion.trace!.displayTip( SegMenTaTion.GpuRow!, CpuFreqExtendStruct.hoverCpuFreqStruct, `${ColorUtils.formatNumberComma(CpuFreqExtendStruct.hoverCpuFreqStruct === undefined ? 0 : CpuFreqExtendStruct.hoverCpuFreqStruct.value!)} Hz·ms` @@ -338,7 +339,7 @@ export class SegMenTaTion { ); SegMenTaTion.GpuRow!.canvasRestore(context); }; - SegMenTaTion.trace.rowsEL?.appendChild(SegMenTaTion.GpuRow); + SegMenTaTion.trace.rowsEL!.appendChild(SegMenTaTion.GpuRow); this.rowFolder!.addChildTraceRow(SegMenTaTion.GpuRow); } @@ -352,10 +353,10 @@ export class SegMenTaTion { SegMenTaTion.schedRow.favoriteChangeHandler = SegMenTaTion.trace.favoriteChangeHandler; SegMenTaTion.schedRow.selectChangeHandler = SegMenTaTion.trace.selectChangeHandler; SegMenTaTion.schedRow.focusHandler = (ev) => { - SegMenTaTion.trace?.displayTip( + SegMenTaTion.trace!.displayTip( SegMenTaTion.schedRow!, CpuFreqExtendStruct.hoverCpuFreqStruct, - `${ColorUtils.formatNumberComma(CpuFreqExtendStruct.hoverCpuFreqStruct?.value!)} Hz·ms` + `${ColorUtils.formatNumberComma(CpuFreqExtendStruct.hoverCpuFreqStruct!.value!)} Hz·ms` ); }; SegMenTaTion.schedRow.findHoverStruct = () => { @@ -381,7 +382,7 @@ export class SegMenTaTion { ); SegMenTaTion.schedRow!.canvasRestore(context); }; - SegMenTaTion.trace.rowsEL?.appendChild(SegMenTaTion.schedRow); + SegMenTaTion.trace.rowsEL!.appendChild(SegMenTaTion.schedRow); this.rowFolder!.addChildTraceRow(SegMenTaTion.schedRow); } @@ -394,18 +395,28 @@ export class SegMenTaTion { SegMenTaTion.binderRow.style.height = '40px'; SegMenTaTion.binderRow.favoriteChangeHandler = SegMenTaTion.trace.favoriteChangeHandler; SegMenTaTion.binderRow.selectChangeHandler = SegMenTaTion.trace.selectChangeHandler; + SegMenTaTion.binderRow.focusHandler = (ev) => { + SegMenTaTion.trace!.displayTip( + SegMenTaTion.binderRow!, + binderStruct.hoverCpuFreqStruct, + `Cycle: ${binderStruct.hoverCpuFreqStruct!.cycle}
+ Name: ${binderStruct.hoverCpuFreqStruct!.name || ''}
+ Count: ${binderStruct.hoverCpuFreqStruct!.value || ''}` + ); + }; SegMenTaTion.binderRow.findHoverStruct = () => { - binderStruct.hoverCpuFreqStruct = SegMenTaTion.binderRow!.getHoverStruct(); + binderStruct.hoverCpuFreqStruct = SegMenTaTion.binderRow!.dataListCache.find((v: any) => { + if (SegMenTaTion.binderRow!.isHover) { + if (v.frame.x < SegMenTaTion.binderRow.hoverX + && v.frame.x + v.frame.width > SegMenTaTion.binderRow.hoverX + && (binderStruct.maxHeight * 20 - v.depth * 20 + 20) < SegMenTaTion.binderRow!.hoverY + && binderStruct.maxHeight * 20 - v.depth * 20 + v.value * 20 + 20 > SegMenTaTion.binderRow!.hoverY) { + console + return v + } + } + }) }; - // SegMenTaTion.binderRow.focusHandler = (ev) => { - // SegMenTaTion.trace?.displayTip( - // SegMenTaTion.binderRow!, - // binderStruct.hoverCpuFreqStruct, - // `Cycle: ${binderStruct.hoverCpuFreqStruct?.cycle}
- // Name: ${binderStruct.hoverCpuFreqStruct?.name || ''}
- // Count: ${binderStruct.hoverCpuFreqStruct?.value || ''}` - // ); - // }; SegMenTaTion.binderRow.supplier = (): Promise> => new Promise>((resolve) => resolve([])); SegMenTaTion.binderRow.onThreadHandler = (useCache) => { @@ -426,7 +437,7 @@ export class SegMenTaTion { ); SegMenTaTion.binderRow!.canvasRestore(context); }; - SegMenTaTion.trace.rowsEL?.appendChild(SegMenTaTion.binderRow); + SegMenTaTion.trace.rowsEL!.appendChild(SegMenTaTion.binderRow); this.rowFolder!.addChildTraceRow(SegMenTaTion.binderRow); } } diff --git a/ide/src/trace/component/setting/SpHilogRecord.ts b/ide/src/trace/component/setting/SpHilogRecord.ts index fbb07bd80a70e4b800db1dfcc0712f44c8ffee12..ef1bb665b7cb1831eacd7b5465101be064e396f5 100644 --- a/ide/src/trace/component/setting/SpHilogRecord.ts +++ b/ide/src/trace/component/setting/SpHilogRecord.ts @@ -55,7 +55,6 @@ export class SpHilogRecord extends BaseElement { configVisibility = 'block'; } if (hiLogConfigList) { - console.log(configVisibility); hiLogConfigList!.forEach(configEl => { configEl.style.display = configVisibility; }); diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index 4992fe7993cf170814d8c1199f004ef204675756..8a93da17ac1ad15fb4e0b890d5163d1b4dc74c44 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -82,6 +82,7 @@ import { type LitPageTable } from '../../../../base-ui/table/LitPageTable.js'; import '../../../../base-ui/popover/LitPopoverV.js'; import { LitPopover } from '../../../../base-ui/popover/LitPopoverV.js'; import { LitTree, TreeItemData } from '../../../../base-ui/tree/LitTree.js'; +import { SegMenTaTion } from '../../chart/SpSegmentationChart.js'; @element('trace-sheet') export class TraceSheet extends BaseElement { @@ -843,6 +844,7 @@ export class TraceSheet extends BaseElement { } loadTabPaneData(key: string): void { + SegMenTaTion.setChartData('BINDER',[]); let component: any = this.shadowRoot ?.querySelector(`#tabs lit-tabpane[key='${key}']`) ?.children.item(0); diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index 797d136b6465d7384d6b44610cc74dddd5c63761..3bfbb6853aba3d1137c94f77051c71eefa64e18d 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -122,6 +122,8 @@ import { TabPaneFreqUsage } from '../sheet/frequsage/TabPaneFreqUsage.js'; import { TabPaneFreqDataCut } from '../sheet/frequsage/TabPaneFreqDataCut.js'; import { TabPaneHisysEvents } from '../sheet/hisysevent/TabPaneHisysEvents.js'; import { TabPaneHiSysEventSummary } from '../sheet/hisysevent/TabPaneHiSysEventSummary.js'; +import { TabPaneThreadBinders } from '../sheet/binder/TabPaneThreadBinders.js'; +import { TabPaneBinderDataCut } from '../sheet/binder/TabPaneBinderDataCut.js'; import {TabPaneGpufreq} from '../sheet/gpufreq/tabPaneGpufreqUsage.js' import {TabPaneGpufreqDataCut} from '../sheet/gpufreq/tabPaneGpufreqDataCut.js' @@ -652,6 +654,16 @@ export let tabConfig: any = { type: TabPaneHiSysEventSummary, require: (param: SelectionParam) => param.hiSysEvents.length > 0, }, + 'tabpane-Thrbinders': { + title: 'Thread Binders', + type: TabPaneThreadBinders, + require: (param: SelectionParam) => param.threadIds.length > 0 + }, + 'tabpane-Thrfreqdatacut': { + title: 'Binder DataCut', + type: TabPaneBinderDataCut, + require: (param: SelectionParam) => param.threadIds.length > 0 + }, 'tabpane-Gpufreq':{ title: 'Gpufreq Usage', type: TabPaneGpufreq, diff --git a/ide/src/trace/component/trace/base/Utils.ts b/ide/src/trace/component/trace/base/Utils.ts index 12cbe77511cfadfcac7771dd57ae6fb5a358f3fb..0728af02678222f0527e0b7053deb254a2f77f45 100644 --- a/ide/src/trace/component/trace/base/Utils.ts +++ b/ide/src/trace/component/trace/base/Utils.ts @@ -97,6 +97,20 @@ export class Utils { } } + public static transferBinderTitle(value: any) { + if (value.startsWith('P-')) { + let pid = value.replace('P-', ''); + let process = Utils.PROCESS_MAP.get(parseInt(pid)) || 'Process'; + return `${process} [${pid}]`; + } else if (value.startsWith('T-')) { + let tid = value.replace('T-', ''); + let thread = Utils.THREAD_MAP.get(parseInt(tid)) || 'Thread'; + return `${thread} [${tid}]`; + } else { + return ''; + } + } + public static getStateColor(state: string): string { if (state === 'D-NIO' || state === 'DK-NIO') { return '#795548'; @@ -483,11 +497,11 @@ export class Utils { } } queryNativeHookResponseTypes(val.leftNs, val.rightNs, types, isStatistic).then((res) => { - procedurePool.submitWithName('logic1', 'native-memory-init-responseType', res, undefined, () => {}); + procedurePool.submitWithName('logic1', 'native-memory-init-responseType', res, undefined, () => { }); }); } - setCurrentSelectIPid(ipid: number): void{ - procedurePool.submitWithName('logic1', 'native-memory-set-current_ipid', ipid, undefined, () => {}); + setCurrentSelectIPid(ipid: number): void { + procedurePool.submitWithName('logic1', 'native-memory-set-current_ipid', ipid, undefined, () => { }); } } diff --git a/ide/src/trace/component/trace/sheet/binder/TabPaneBinderDataCut.ts b/ide/src/trace/component/trace/sheet/binder/TabPaneBinderDataCut.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3cbaa9c9782b89b3da7894f8b74f9e44b7b9147 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/binder/TabPaneBinderDataCut.ts @@ -0,0 +1,781 @@ +/* + * 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.js'; +import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table.js'; +import { Utils } from '../../base/Utils.js'; +import { SelectionParam } from '../../../../bean/BoxSelection'; +import { BinderGroup, DataSource, FuncNameCycle, BinderColumn } from '../../../../bean/BinderProcessThread.js'; +import { querySingleFuncNameCycle, queryBinderByThreadId, queryLoopFuncNameCycle } from '../../../../database/SqlLite.js'; +import { resizeObserver } from '../SheetUtils.js'; +import { LitChartColumn } from '../../../../../base-ui/chart/column/LitChartColumn.js'; +import '../../../../../base-ui/chart/column/LitChartColumn.js'; +// import { SegMenTaTion } from '../../../chart/SpSegmentationChart.js'; + +@element('tabpane-binder-datacut') +export class TabPaneBinderDataCut extends BaseElement { + private threadBindersTbl: LitTable | null | undefined; + private currentSelectionParam: SelectionParam | any; + private threadStatesDIV: Element | null | undefined; + private cycleARangeArr: BinderGroup[] | undefined; + private cycleBRangeArr: BinderGroup[] | undefined; + private cycleAStartRangeDIV: HTMLInputElement | null | undefined; + private cycleAEndRangeDIV: HTMLInputElement | null | undefined; + private cycleBStartRangeDIV: HTMLInputElement | null | undefined; + private cycleBEndRangeDIV: HTMLInputElement | null | undefined; + private chartTotal: LitChartColumn | null | undefined; + private dataSource: DataSource[] | undefined; + private rowCycleData: BinderGroup[] | undefined; + private funcNameCycleArr: FuncNameCycle[] | undefined; + private cacheBinderArr: BinderGroup[] | undefined; + private currentThreadId: string | undefined; + + set data(threadStatesParam: SelectionParam | any) { + let threadIdDIV = this.shadowRoot!.querySelector('.thread-id-input') as HTMLElement; + threadIdDIV.style.border = '1px solid rgb(151,151,151)'; + let cycleNameDIV = this.shadowRoot!.querySelector('.cycle-name-input') as HTMLElement; + cycleNameDIV.style.border = '1px solid rgb(151,151,151)'; + if (this.currentSelectionParam === threadStatesParam) { + return; + } + this.dispalyQueryArea(true); + this.clickLoop(false); + this.clickSingle(false); + this.currentSelectionParam = threadStatesParam; + this.threadBindersTbl!.recycleDataSource = []; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } + + dispalyQueryArea(b: boolean) { + if (b) { + this.setAttribute('dispalyQueryArea', ''); + } else { + this.removeAttribute('dispalyQueryArea'); + } + } + + clickSingle(b: boolean) { + if (b) { + this.setAttribute('clickSingle', ''); + } else { + this.removeAttribute('clickSingle'); + } + } + + clickLoop(b: boolean) { + if (b) { + this.setAttribute('clickLoop', ''); + } else { + this.removeAttribute('clickLoop'); + } + } + + async dataLoopCut(threadId: HTMLInputElement, threadFunc: HTMLInputElement) { + this.currentThreadId = ''; + let threadIds = this.currentSelectionParam.threadIds; + let processIds = this.currentSelectionParam.processIds; + let threadIdValue = threadId.value.trim(); + let threadFuncName = threadFunc.value.trim(); + let leftNS = this.currentSelectionParam.leftNs; + let rightNS = this.currentSelectionParam.rightNs; + if (threadIdValue != '' && threadFuncName != '') { + // SegMenTaTion.setChartData("BINDER", []); + this.clickLoop(true); + this.clickSingle(false); + this.threadBindersTbl!.loading = true; + threadId.style.border = '1px solid rgb(151,151,151)'; + threadFunc.style.border = '1px solid rgb(151,151,151)'; + this.funcNameCycleArr = await queryLoopFuncNameCycle(threadFuncName, threadIdValue, leftNS, rightNS); + let binderArr = await queryBinderByThreadId(processIds, threadIds, leftNS, rightNS); + if (this.funcNameCycleArr.length !== 0) { + let binderCutArr = []; + for (let j = 0; j < this.funcNameCycleArr.length - 1; j++) { + this.funcNameCycleArr[j].cycleDur = this.funcNameCycleArr[j + 1].cycleStartTime - this.funcNameCycleArr[j].cycleStartTime; + for (let i = 0; i < binderArr.length; i++) { + if (binderArr[i].ts > this.funcNameCycleArr[j].cycleStartTime + && binderArr[i].ts + binderArr[i].dur < this.funcNameCycleArr[j + 1].cycleStartTime) { + // calculate cycle duration + binderArr[i].cycleDur = this.funcNameCycleArr[j + 1].cycleStartTime - this.funcNameCycleArr[j].cycleStartTime; + binderArr[i].cycleStartTime = this.funcNameCycleArr[j].cycleStartTime; + binderArr[i].funcName = this.funcNameCycleArr[j].funcName; + binderArr[i].id = this.funcNameCycleArr[j].id; + binderArr[i].thread = Utils.THREAD_MAP.get(binderArr[i].tid) || 'Thread'; + binderArr[i].process = Utils.PROCESS_MAP.get(binderArr[i].pid) || 'Process'; + binderArr[i].count = 1; + binderCutArr.push(binderArr[i]); + } + } + } + let finalCutBinderArr = this.completionCycleName(binderCutArr, 'loop'); + this.threadBindersTbl!.recycleDataSource = this.transferToTreeData(finalCutBinderArr); + this.threadBindersTbl!.loading = false; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } else { + this.threadBindersTbl!.recycleDataSource = []; + this.threadBindersTbl!.loading = false; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } + } else { + if (threadIdValue == '') { + threadId.style.border = '1px solid rgb(255,0,0)'; + threadId.setAttribute('placeholder', 'Please input thread id'); + } else { + threadId.style.border = '1px solid rgb(151,151,151)'; + } + + if (threadFuncName == '') { + threadFunc.style.border = '1px solid rgb(255,0,0)'; + threadFunc.setAttribute('placeholder', 'Please input function name'); + } else { + threadFunc.style.border = '1px solid rgb(151,151,151)'; + } + } + } + + async dataSingleCut(threadId: HTMLInputElement, threadFunc: HTMLInputElement) { + this.currentThreadId = ''; + let threadIds = this.currentSelectionParam.threadIds; + let processIds = this.currentSelectionParam.processIds; + let threadIdValue = threadId.value.trim(); + let threadFuncName = threadFunc.value.trim(); + let leftNS = this.currentSelectionParam.leftNs; + let rightNS = this.currentSelectionParam.rightNs; + if (threadIdValue != '' && threadFuncName != '') { + // SegMenTaTion.setChartData("BINDER", []); + this.clickLoop(false); + this.clickSingle(true); + threadId.style.border = '1px solid rgb(151,151,151)'; + threadFunc.style.border = '1px solid rgb(151,151,151)'; + this.threadBindersTbl!.loading = true; + this.funcNameCycleArr = await querySingleFuncNameCycle(threadFuncName, threadIdValue, leftNS, rightNS); + let binderArr = await queryBinderByThreadId(processIds, threadIds, leftNS, rightNS); + if (this.funcNameCycleArr.length !== 0) { + let binderCutArr: BinderGroup[] = []; + for (let j = 0; j < this.funcNameCycleArr.length; j++) { + for (let i = 0; i < binderArr.length; i++) { + if (binderArr[i].ts > this.funcNameCycleArr[j].cycleStartTime + && binderArr[i].ts + binderArr[i].dur < this.funcNameCycleArr[j].cycleStartTime + this.funcNameCycleArr[j]!.cycleDur) { + binderArr[i].cycleDur = this.funcNameCycleArr[j].cycleDur; + binderArr[i].cycleStartTime = this.funcNameCycleArr[j].cycleStartTime; + binderArr[i].funcName = this.funcNameCycleArr[j].funcName; + binderArr[i].id = this.funcNameCycleArr[j].id; + binderArr[i].thread = Utils.THREAD_MAP.get(binderArr[i].tid) || 'Thread'; + binderArr[i].process = Utils.PROCESS_MAP.get(binderArr[i].pid) || 'Process'; + binderArr[i].count = 1; + binderCutArr.push(binderArr[i]); + } + } + } + + let finalCutBinderArr = this.completionCycleName(binderCutArr, 'single'); + this.threadBindersTbl!.recycleDataSource = this.transferToTreeData(finalCutBinderArr); + this.threadBindersTbl!.loading = false; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } else { + this.threadBindersTbl!.recycleDataSource = []; + this.threadBindersTbl!.loading = false; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } + } else { + if (threadIdValue == '') { + threadId.style.border = '1px solid rgb(255,0,0)'; + threadId.setAttribute('placeholder', 'Please input thread id'); + } else { + threadId.style.border = '1px solid rgb(151,151,151)'; + } + + if (threadFuncName == '') { + threadFunc.style.border = '1px solid rgb(255,0,0)'; + threadFunc.setAttribute('placeholder', 'Please input function name'); + } else { + threadFunc.style.border = '1px solid rgb(151,151,151)'; + } + } + } + + completionCycleName(binderCutArr: BinderGroup[], type: string): BinderGroup[] { + let threadIds: number[] = this.currentSelectionParam.threadIds; + let threadBinderCutArr: BinderGroup[][] = []; + let childThreadbinderCutArr: BinderGroup[] = []; + threadIds.forEach((tid: number, i: number) => { + childThreadbinderCutArr = []; + binderCutArr.forEach((binder: BinderGroup) => { + if (binder.tid === tid) { + childThreadbinderCutArr.push(binder) + } + }) + if (childThreadbinderCutArr.length > 0) { + threadBinderCutArr.push(childThreadbinderCutArr); + } + }) + // loop data cut need delete last function Name cycle data + if (type === 'loop') { + this.funcNameCycleArr?.pop(); + } + let cloneThreadBinderCutArr: BinderGroup[][] = JSON.parse(JSON.stringify(threadBinderCutArr)); + let binderContainsFuncIdArr: any = []; + threadBinderCutArr.forEach((binderArr: BinderGroup[], idx: number) => { + binderArr.forEach((binder: BinderGroup) => { + if (!binderContainsFuncIdArr.includes(binder.id)) { + binderContainsFuncIdArr.push(binder.id); + } + }) + // When the cycle data is incomplete + if (this.funcNameCycleArr!.length !== binderContainsFuncIdArr.length) { + this.completionAllFname(binderContainsFuncIdArr, idx, cloneThreadBinderCutArr); + } + binderContainsFuncIdArr = []; + }) + cloneThreadBinderCutArr.forEach(arr => { + arr.sort(this.compare('id')); + }) + return cloneThreadBinderCutArr.flat(); + } + + completionAllFname(binderContainsFuncIdArr: number[], idx: number, cloneThreadBinderCutArr: BinderGroup[][]) { + this.funcNameCycleArr!.forEach((it: FuncNameCycle) => { + if (!binderContainsFuncIdArr.includes(it.id)) { + let itemData = cloneThreadBinderCutArr[idx][0]; + let item: BinderGroup = { + title: '', + count: 0, + totalCount: 0, + cycleDur: it.cycleDur, + cycleStartTime: it.cycleStartTime, + id: it.id, + name: '', + pid: itemData.pid, + process: itemData.process, + thread: itemData.thread, + tid: itemData.tid, + idx: 0 + } + cloneThreadBinderCutArr[idx].push(item); + } + }) + } + + compare(prop: string) { + return function (obj1: any, obj2: any) { + let val1 = obj1[prop]; + let val2 = obj2[prop]; + if (val1 < val2) { + return -1; + } else if (val1 > val2) { + return 1; + } else { + return 0; + } + } + } + + transferToTreeData(binderList: Array): Array { + let group: any = {}; + binderList.forEach((it: BinderGroup) => { + let cycleItem = { + title: it.thread + ' ' + '[' + it.tid + ']' + '[' + it.id + ']', + totalCount: it.count, + binderTransactionCount: it.name == 'binder transaction' ? it.count : 0, + binderAsyncRcvCount: it.name == 'binder async rcv' ? it.count : 0, + binderReplyCount: it.name == 'binder reply' ? it.count : 0, + binderTransactionAsyncCount: it.name == 'binder transaction async' ? it.count : 0, + tid: it.tid, + pid: it.pid, + thread: it.thread, + cycleDur: it.cycleDur || 0, + cycleStartTime: it.cycleStartTime, + type: 'cycle', + }; + if (group[`${it.pid}`]) { + let process = group[`${it.pid}`]; + process.totalCount += it.count; + let thread = process.children.find((child: BinderGroup) => child.title === it.thread + ' ' + '[' + it.tid + ']'); + if (thread) { + thread.totalCount += it.count; + let cycle = thread.children.find((child: BinderGroup) => child.title === it.thread + ' ' + '[' + it.tid + ']' + '[' + it.id + ']'); + if (cycle) { + cycle.totalCount += it.count; + cycle.binderTransactionCount += it.name == 'binder transaction' ? it.count : 0; + cycle.binderAsyncRcvCount += it.name == 'binder async rcv' ? it.count : 0; + cycle.binderReplyCount += it.name == 'binder reply' ? it.count : 0; + cycle.binderTransactionAsyncCount += it.name == 'binder transaction async' ? it.count : 0; + } else { + thread.children.push(cycleItem); + } + } else { + process.children.push({ + title: it.thread + ' ' + '[' + it.tid + ']', + totalCount: it.count, + tid: it.tid, + pid: it.pid, + type: 'thread', + children: [cycleItem], + }) + } + } else { + group[`${it.pid}`] = { + title: it.process + ' ' + '[' + it.pid + ']', + totalCount: it.count, + tid: it.tid, + pid: it.pid, + type: 'process', + children: [ + { + title: it.thread + ' ' + '[' + it.tid + ']', + totalCount: it.count, + tid: it.tid, + pid: it.pid, + type: 'thread', + children: [cycleItem], + } + ] + } + } + }); + this.cacheBinderArr = JSON.parse(JSON.stringify(this.addCycleNumber(Object.values(group)))); + let groupArr = this.timeUnitConversion(Object.values(group)) + return groupArr; + } + + addCycleNumber(groupArr: Array): Array { + for (let i = 0; i < groupArr.length; i++) { + if (groupArr[i].type === 'cycle') { + groupArr[i].title = 'cycle ' + (i + 1) + '_' + groupArr[i].thread + ' ' + '[' + groupArr[i].tid + ']'; + groupArr[i].idx = i + 1; + } else { + this.addCycleNumber(groupArr[i].children!) + } + } + return groupArr + } + + timeUnitConversion(groupArr: Array): Array { + for (let i = 0; i < groupArr.length; i++) { + if (groupArr[i].type === 'cycle') { + groupArr[i].cycleDur = Number((groupArr[i].cycleDur / 1000000).toFixed(3)); + groupArr[i].cycleStartTime = Number((groupArr[i].cycleStartTime / 1000000).toFixed(3)); + } else { + this.timeUnitConversion(groupArr[i].children!) + } + } + return groupArr + } + + private theadClick(data: Array): void { + let labels = this.threadBindersTbl?.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label'); + if (labels) { + for (let i = 0; i < labels.length; i++) { + let label = labels[i].innerHTML; + labels[i].addEventListener('click', (e) => { + if (label.includes('Process') && i === 0) { + this.threadBindersTbl!.setStatus(data, false); + this.threadBindersTbl!.recycleDs = this.threadBindersTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (label.includes('Thread') && i === 1) { + for (let item of data) { + item.status = true; + if (item.children != undefined && item.children.length > 0) { + this.threadBindersTbl!.setStatus(item.children, false); + } + } + this.threadBindersTbl!.recycleDs = this.threadBindersTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (label.includes('Cycle') && i === 2) { + this.threadBindersTbl!.setStatus(data, true); + this.threadBindersTbl!.recycleDs = this.threadBindersTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand); + } + }); + } + } + } + + binderWithCountList(rowCycleData: BinderGroup[]): Array { + let binderWithCountList: Array = []; + rowCycleData.forEach(it => { + if (it.totalCount !== 0) { + let cycleDataArr: BinderColumn[] = []; + if (it.binderTransactionCount !== 0) { + cycleDataArr.push({ + name: 'binder transaction', + count: it.binderTransactionCount!, + dur: it.cycleDur, + startNS: it.cycleStartTime, + idx: it.idx + }) + } + if (it.binderTransactionAsyncCount !== 0) { + cycleDataArr.push({ + name: 'binder transaction async', + count: it.binderTransactionAsyncCount!, + dur: it.cycleDur, + startNS: it.cycleStartTime, + idx: it.idx + }) + } + if (it.binderReplyCount !== 0) { + cycleDataArr.push({ + name: 'binder reply', + count: it.binderReplyCount!, + dur: it.cycleDur, + startNS: it.cycleStartTime, + idx: it.idx + }) + } + if (it.binderAsyncRcvCount !== 0) { + cycleDataArr.push({ + name: 'binder async rcv', + count: it.binderAsyncRcvCount!, + dur: it.cycleDur, + startNS: it.cycleStartTime, + idx: it.idx + }) + } + binderWithCountList.push(cycleDataArr) + } + }) + return binderWithCountList; + } + + findThreadByThreadId(groupArr: Array, threadId: number): BinderGroup[] { + let currentSelectThread: BinderGroup[] = []; + groupArr.forEach(p => { + p.children?.forEach(th => { + if (th.tid === threadId) { + currentSelectThread = th.children! + } + }) + }) + return currentSelectThread + } + + initElements(): void { + this.threadBindersTbl = this.shadowRoot?.querySelector('#tb-binder-count'); + this.chartTotal = this.shadowRoot!.querySelector('#chart_cycle'); + this.cycleAStartRangeDIV = this.shadowRoot?.querySelector('#cycle-a-start-range'); + this.cycleAEndRangeDIV = this.shadowRoot?.querySelector('#cycle-a-end-range'); + this.cycleBStartRangeDIV = this.shadowRoot?.querySelector('#cycle-b-start-range'); + this.cycleBEndRangeDIV = this.shadowRoot?.querySelector('#cycle-b-end-range'); + + this.threadStatesDIV = this.shadowRoot!.querySelector('#dataCut'); + this.threadStatesDIV?.children[2].children[0].addEventListener('click', (e) => { + this.dispalyQueryArea(true); + this.dataSource = []; + // @ts-ignore + this.dataSingleCut(this.threadStatesDIV!.children[0], this.threadStatesDIV?.children[1]); + }) + this.threadStatesDIV?.children[2].children[1].addEventListener('click', (e) => { + this.dispalyQueryArea(true); + this.dataSource = []; + // @ts-ignore + this.dataLoopCut(this.threadStatesDIV?.children[0], this.threadStatesDIV?.children[1]); + }) + + this.threadBindersTbl!.addEventListener('row-click', (evt: any) => { + let currentData = evt.detail.data; + if (currentData.type === 'thread') { + this.currentThreadId = currentData.tid + '' + currentData.pid; + this.clearCycleRange(); + currentData.isSelected = true; + this.threadBindersTbl!.clearAllSelection(currentData); + this.threadBindersTbl!.setCurrentSelection(currentData); + this.rowCycleData = currentData.children; + this.dispalyQueryArea(false); + let totalCount = currentData.totalCount; + this.dataSource = []; + this.dataSource.push({ + xName: 'Total', + yAverage: totalCount !== 0 ? Math.ceil(totalCount / this.rowCycleData!.length) : 0 + }) + if (this.dataSource!.length !== 0) { + this.drawColumn(); + } + let threaId = currentData.tid; + let rowThreadBinderArr = this.findThreadByThreadId(this.cacheBinderArr!, threaId); + let binderList = this.binderWithCountList(rowThreadBinderArr!); + // SegMenTaTion.setChartData('BINDER', binderList); + } + + if (currentData.type === 'cycle' && currentData.tid + '' + currentData.pid === this.currentThreadId) { + currentData.isSelected = true; + this.threadBindersTbl!.clearAllSelection(currentData); + this.threadBindersTbl!.setCurrentSelection(currentData); + // SegMenTaTion.tabHover('BINDER', true, currentData.idx); + } + }); + + this.shadowRoot?.querySelector('#query-btn')?.addEventListener('click', () => { + this.cycleARangeArr = this.rowCycleData?.filter((it: BinderGroup) => { + return it.cycleDur >= Number(this.cycleAStartRangeDIV!.value) + && it.cycleDur < Number(this.cycleAEndRangeDIV!.value); + }) + this.cycleBRangeArr = this.rowCycleData?.filter((it: BinderGroup) => { + return it.cycleDur >= Number(this.cycleBStartRangeDIV!.value) + && it.cycleDur < Number(this.cycleBEndRangeDIV!.value); + }) + let cycleACount = 0; + this.cycleARangeArr?.forEach((it: BinderGroup) => { + cycleACount += it.totalCount; + }) + let cycleBCount = 0; + this.cycleBRangeArr?.forEach((it: BinderGroup) => { + cycleBCount += it.totalCount; + }) + this.dataSource!.length > 1 && this.dataSource?.splice(1); + this.dataSource!.push({ + xName: 'cycleA', + yAverage: cycleACount !== 0 ? Math.ceil(cycleACount / this.cycleARangeArr!.length) : 0 + }) + this.dataSource!.push({ + xName: 'cycleB', + yAverage: cycleBCount !== 0 ? Math.ceil(cycleBCount / this.cycleBRangeArr!.length) : 0 + }) + if (this.dataSource!.length !== 0) { + this.drawColumn(); + } + }) + } + + clearCycleRange(): void { + this.cycleAStartRangeDIV!.value = ''; + this.cycleAEndRangeDIV!.value = ''; + this.cycleBStartRangeDIV!.value = ''; + this.cycleBEndRangeDIV!.value = ''; + } + + drawColumn(): void { + this.chartTotal!.dataSource = this.dataSource!; + this.chartTotal!.config = { + data: this.dataSource!, + appendPadding: 10, + xField: 'xName', + yField: 'yAverage', + seriesField: '', + removeUnit: true, + notSort: true, + color: (a) => { + if (a.xName === 'Total') { + return '#2f72f8'; + } else if (a.xName === 'cycleA') { + return '#ffab67'; + } else if (a.xName === 'cycleB') { + return '#a285d2'; + } else { + return '#0a59f7'; + } + }, + tip: (a) => { + if (a && a[0]) { + let tip = ''; + tip = `
+
Average: ${a[0].obj.yAverage}
+
`; + return tip; + } else { + return ''; + } + }, + label: null, + }; + } + + connectedCallback() { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.threadBindersTbl!); + } + + initHtml(): string { + return ` + +
+ + +
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+
+
+ Cycle A: + + ~ + +
+
+
+ Cycle B: + + ~ + +
+
+ +
+
+
+
+
Average Binder Count
+ +
+
Total
+
CycleA
+
CycleB
+
+
+
+
+
+ ` + } +} \ No newline at end of file diff --git a/ide/src/trace/component/trace/sheet/binder/TabPaneThreadBinders.ts b/ide/src/trace/component/trace/sheet/binder/TabPaneThreadBinders.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3a464198456c13e1d3a1f19137a25e3d9776522 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/binder/TabPaneThreadBinders.ts @@ -0,0 +1,180 @@ +/* + * 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.js'; +import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table.js'; +import { SelectionParam } from '../../../../bean/BoxSelection.js'; +import '../../../StackBar.js' +import { getTabBindersCount } from '../../../../database/SqlLite.js'; +import { Utils } from '../../base/Utils.js'; +import { resizeObserver } from '../SheetUtils.js'; +import { BinderGroup } from '../../../../bean/BinderProcessThread.js'; + +@element('tabpane-thread-binder') +export class TabPaneThreadBinders extends BaseElement { + private threadBindersTbl: LitTable | null | undefined; + private currentSelectionParam: Selection | undefined; + + set data(threadStatesParam: SelectionParam | any) { + if (this.currentSelectionParam === threadStatesParam) { + return; + } + this.threadBindersTbl!.loading = true; + this.currentSelectionParam = threadStatesParam; + this.threadBindersTbl!.recycleDataSource = []; + let binderList: BinderGroup[] = []; + let threadIds = threadStatesParam.threadIds; + let processIds: any[] = [...new Set(threadStatesParam.processIds)]; + getTabBindersCount(processIds, threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs).then((result) => { + if (result != null && result.length > 0 && result[0].count != 0) { + binderList = result; + } + if (binderList.length > 0) { + this.timeUnitConversion(binderList); + this.threadBindersTbl!.recycleDataSource = this.transferToTreeData(binderList); + this.threadBindersTbl!.loading = false; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } else if (binderList.length === 0) { + this.threadBindersTbl!.recycleDataSource = []; + this.threadBindersTbl!.loading = false; + this.theadClick(this.threadBindersTbl!.recycleDataSource); + } + }) + } + + timeUnitConversion(binderList: Array) { + binderList.forEach(b => { + b.cycleDur = Number((b.cycleDur / 1000000).toFixed(3)); + b.cycleStartTime = Number((b.cycleStartTime / 1000000).toFixed(3)); + }) + } + + initElements(): void { + this.threadBindersTbl = this.shadowRoot?.querySelector('#tb-binder-count'); + this.threadBindersTbl!.itemTextHandleMap.set('title', Utils.transferBinderTitle); + } + + connectedCallback() { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.threadBindersTbl!); + } + + transferToTreeData(binderList: BinderGroup[]): BinderGroup[] { + let group: any = {}; + binderList.forEach((it: BinderGroup) => { + if (group[`${it.pid}`]) { + let process = group[`${it.pid}`]; + process.totalCount += it.count; + let thread = process.children.find((child: BinderGroup) => child.title === `T-${it.tid}`); + if (thread) { + thread.totalCount += it.count; + thread.binderTransactionCount += it.name == 'binder transaction' ? it.count : 0; + thread.binderAsyncRcvCount += it.name == 'binder async rcv' ? it.count : 0; + thread.binderReplyCount += it.name == 'binder reply' ? it.count : 0; + thread.binderTransactionAsyncCount += it.name == 'binder transaction async' ? it.count : 0; + } else { + process.children.push({ + title: `T-${it.tid}`, + totalCount: it.count, + binderTransactionCount: it.name == 'binder transaction' ? it.count : 0, + binderAsyncRcvCount: it.name == 'binder async rcv' ? it.count : 0, + binderReplyCount: it.name == 'binder reply' ? it.count : 0, + binderTransactionAsyncCount: it.name == 'binder transaction async' ? it.count : 0, + tid: it.tid, + pid: it.pid + }) + } + } else { + group[`${it.pid}`] = { + title: `P-${it.pid}`, + totalCount: it.count, + tid: it.tid, + pid: it.pid, + cycleDur: it.cycleDur || 0, + cycleStartTime: it.cycleStartTime, + children: [ + { + title: `T-${it.tid}`, + totalCount: it.count, + binderTransactionCount: it.name == 'binder transaction' ? it.count : 0, + binderAsyncRcvCount: it.name == 'binder async rcv' ? it.count : 0, + binderReplyCount: it.name == 'binder reply' ? it.count : 0, + binderTransactionAsyncCount: it.name == 'binder transaction async' ? it.count : 0, + tid: it.tid, + pid: it.pid, + } + ] + } + } + }); + return Object.values(group); + } + + private theadClick(data: Array) { + let labels = this.threadBindersTbl?.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label'); + if (labels) { + for (let i = 0; i < labels.length; i++) { + let label = labels[i].innerHTML; + labels[i].addEventListener('click', (e) => { + if (label.includes('Process') && i === 0) { + this.threadBindersTbl!.setStatus(data, false); + this.threadBindersTbl!.recycleDs = this.threadBindersTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (label.includes('Thread') && i === 1) { + for (let item of data) { + item.status = true; + if (item.children != undefined && item.children.length > 0) { + this.threadBindersTbl!.setStatus(item.children, false); + } + } + this.threadBindersTbl!.recycleDs = this.threadBindersTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } + }); + } + } + } + + + initHtml(): string { + return ` + + + + + + + + + + + + + + + + + ` + } +} \ No newline at end of file diff --git a/ide/src/trace/database/SqlLite.ts b/ide/src/trace/database/SqlLite.ts index 03a1983510b1b5f1c45f44b1a912566c69961e5c..1f87d94e6c677eb0c95a9ce0fb98f4c7d332a66b 100644 --- a/ide/src/trace/database/SqlLite.ts +++ b/ide/src/trace/database/SqlLite.ts @@ -90,6 +90,7 @@ import { type SnapshotStruct } from './ui-worker/ProcedureWorkerSnapshot.js'; import { type MemoryConfig } from '../bean/MemoryConfig.js'; import { LogStruct } from './ui-worker/ProcedureWorkerLog.js'; import { HiSysEventStruct } from './ui-worker/ProcedureWorkerHiSysEvent.js'; +import { FuncNameCycle } from '../bean/BinderProcessThread.js'; class DataWorkerThread extends Worker { taskMap: any = {}; @@ -297,8 +298,8 @@ export class DbPool { } } }; - thread!.onmessageerror = (e) => {}; - thread!.onerror = (e) => {}; + thread!.onmessageerror = (e) => { }; + thread!.onerror = (e) => { }; thread!.id = i; thread!.busy = false; this.works?.push(thread!); @@ -723,8 +724,8 @@ export const getTabFps = (leftNs: number, rightNs: number): Promise> { $leftNS: leftNs, $rightNS: rightNs } ); - export const getTabCounters = (processFilterIds: Array, virtualFilterIds: Array, startTime: number) => { - let processSql = `select +export const getTabCounters = (processFilterIds: Array, virtualFilterIds: Array, startTime: number) => { + let processSql = `select t1.filter_id as trackId, t2.name, value, @@ -740,8 +741,8 @@ export const getTabFps = (leftNs: number, rightNs: number): Promise> where filter_id in (${processFilterIds.join(',')}) and - startTime <= ${startTime}` ; - let virtualSql = `select + startTime <= ${startTime}`; + let virtualSql = `select t1.filter_id as trackId, t2.name, value, @@ -758,18 +759,18 @@ export const getTabFps = (leftNs: number, rightNs: number): Promise> filter_id in (${virtualFilterIds.join(',')}) and startTime <= ${startTime}`; - let sql = ''; - if (processFilterIds.length > 0 && virtualFilterIds.length > 0) { - sql = `${processSql} union ${virtualSql}`; + let sql = ''; + if (processFilterIds.length > 0 && virtualFilterIds.length > 0) { + sql = `${processSql} union ${virtualSql}`; + } else { + if (processFilterIds.length > 0) { + sql = processSql; } else { - if (processFilterIds.length > 0) { - sql = processSql; - } else { - sql = virtualSql; - } + sql = virtualSql; } - return query('getTabCounters', sql, {}); } + return query('getTabCounters', sql, {}); +} export const getTabVirtualCounters = (virtualFilterIds: Array, startTime: number) => query( @@ -1351,8 +1352,7 @@ export const queryVirtualMemory = (): Promise> => export const queryVirtualMemoryData = (filterId: number): Promise> => query( 'queryVirtualMemoryData', - `select ts-${ - (window as any).recordStartNS + `select ts-${(window as any).recordStartNS } as startTime,value,filter_id as filterID from sys_mem_measure where filter_id=$filter_id`, { $filter_id: filterId } ); @@ -1428,7 +1428,7 @@ order by start_name;`, { $pid: pid } ); - export const queryProcessAllAppStartup = (pids: Array): Promise> => +export const queryProcessAllAppStartup = (pids: Array): Promise> => query( 'queryProcessStartup', ` @@ -1449,7 +1449,7 @@ order by start_name;`, { $pid: pids } ); - export const querySingleAppStartupsName = (pid:number): Promise> => +export const querySingleAppStartupsName = (pid: number): Promise> => query( 'queryAllAppStartupsName', `select name from process @@ -3995,11 +3995,9 @@ export const queryEbpfSamplesCount = (startTime: number, endTime: number, ipids: select fsCount, vmCount from -(select count(1) as fsCount from file_system_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ - ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' +(select count(1) as fsCount from file_system_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' }) -,(select count(1) as vmCount from paged_memory_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ - ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' +,(select count(1) as vmCount from paged_memory_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' }); `, { $startTime: startTime, $endTime: endTime } @@ -5648,9 +5646,6 @@ export const queryTraceType = (): Promise< m.name = 'source_type';` ); -export const queryTransferList = (): Promise> => - query('queryTransferList', `select id, report_value as cmdStr from perf_report where report_type = 'config_name'`); - export const getTabRunningPercent = (tIds: Array, leftNS: number, rightNS: number): Promise> => query( 'getTabRunningPercent', @@ -5771,15 +5766,15 @@ export const queryHiSysEventData = (): Promise> => ORDER BY S.ts` ); - export const querySearchRowFuncData = ( - funcName: string, - tIds: number, - leftNS: number, - rightNS: number - ): Promise> => - query( - 'querySearchRowFuncData', - ` +export const querySearchRowFuncData = ( + funcName: string, + tIds: number, + leftNS: number, + rightNS: number +): Promise> => + query( + 'querySearchRowFuncData', + ` select c.name as funName, c.ts - r.start_ts as startTime, @@ -5805,8 +5800,180 @@ export const queryHiSysEventData = (): Promise> => and not ((startTime < ${leftNS}) or (startTime > ${rightNS})); `, - { $search: funcName } - ); + { $search: funcName } + ); + +export const queryTransferList = (): Promise> => + query( + 'queryTransferList', + `SELECT + id, + report_value as cmdStr + FROM + perf_report + WHERE + report_type = 'config_name'` + ); + +export const getTabBindersCount = (pIds: number[], tIds: number[], leftNS: number, rightNS: number): Promise> => + query( + 'getTabBindersCount', + ` + SELECT + c.name, + c.dur, + 1 AS count, + c.ts, + c.ts - r.start_ts AS startTime, + c.ts -r.start_ts + c.dur AS endTime, + t.tid, + p.pid, + ${leftNS} AS cycleStartTime, + (${rightNS} - ${leftNS}) AS cycleDur + FROM + callstack c, trace_range r + LEFT JOIN + thread t + ON + c.callid = t.id + LEFT JOIN + process p + ON + t.ipid = p.id + WHERE + c.name in ('binder transaction', 'binder async rcv', 'binder reply', 'binder transaction async') + AND + t.tid in (${tIds.join(',')}) + AND + p.pid in (${pIds.join(',')}) + AND NOT + ((startTime < ${leftNS}) + OR + (endTime > ${rightNS})); + `, + { + $leftNS: leftNS, + $rightNS: rightNS + } + ); + +export const queryBinderByThreadId = (pIds: number[], tIds: Array, leftNS: number, rightNS: number): Promise> => + query( + 'queryBinderByThreadId', + ` + SELECT + c.name, + c.ts - r.start_ts AS ts, + c.dur, + c.ts - r.start_ts AS startTime, + c.ts - r.start_ts + c.dur AS endTime, + t.tid, + p.pid + FROM + callstack c, trace_range r + LEFT JOIN + thread t + ON + c.callid = t.id + LEFT JOIN + process p + ON + t.ipid = p.id + WHERE + c.name in ('binder transaction', 'binder async rcv', 'binder reply', 'binder transaction async') + AND + t.tid in (${tIds.join(',')}) + AND + p.pid in (${pIds.join(',')}) + AND NOT + ((startTime < ${leftNS}) + OR + (endTime > ${rightNS})) + `, + { + $tIds: tIds, + $leftNS: leftNS, + $rightNS: rightNS + } + ); + + +export const querySingleFuncNameCycle = (funcName: string, tIds: string, leftNS: number, rightNS: number): Promise> => + query( + 'querySingleFuncNameCycle', + ` + SELECT + c.name AS funcName, + c.ts - r.start_ts AS cycleStartTime, + c.dur AS cycleDur, + c.id, + t.tid, + p.pid, + c.ts - r.start_ts + c.dur AS endTime + FROM + callstack c, trace_range r + LEFT JOIN + thread t + ON + c.callid = t.id + LEFT JOIN + process p + ON + t.ipid = p.id + WHERE + c.name = '${funcName}' + AND + t.tid = ${tIds} + AND NOT + ((cycleStartTime < ${leftNS}) + OR + (endTime > ${rightNS})) + `, + { + $funcName: funcName, + $tIds: tIds, + $leftNS: leftNS, + $rightNS: rightNS + } + ); + +export const queryLoopFuncNameCycle = (funcName: string, tIds: string, leftNS: number, rightNS: number): Promise> => + query( + 'queryLoopFuncNameCycle', + ` + SELECT + c.name AS funcName, + c.ts - r.start_ts AS cycleStartTime, + 0 AS cycleDur, + c.id, + t.tid, + p.pid + FROM + callstack c, trace_range r + LEFT JOIN + thread t + ON + c.callid = t.id + LEFT JOIN + process p + ON + t.ipid = p.id + WHERE + c.name = '${funcName}' + AND + t.tid = ${tIds} + AND NOT + ((cycleStartTime < ${leftNS}) + OR + (cycleStartTime > ${rightNS})) + `, + { + $funcName: funcName, + $tIds: tIds, + $leftNS: leftNS, + $rightNS: rightNS + } + ); export const getGpufreqData = (leftNS: number, rightNS: number, earliest: boolean): Promise> => { let queryCondition = ''; @@ -5911,4 +6078,4 @@ export const queryHiSysEventData = (): Promise> => `, { $search: funcName } ); - } \ No newline at end of file + } diff --git a/ide/src/trace/database/ui-worker/procedureWorkerBinder.ts b/ide/src/trace/database/ui-worker/procedureWorkerBinder.ts index af4af2d0cbabe7a1ddbc93aa6d281926f7356e83..ee20c3fb603cb9e7fe8e28deadadffc45f8e3397 100644 --- a/ide/src/trace/database/ui-worker/procedureWorkerBinder.ts +++ b/ide/src/trace/database/ui-worker/procedureWorkerBinder.ts @@ -41,10 +41,14 @@ export class BinderRender extends Render { }); freqReq.context.beginPath(); for (let re of freqFilter) { - if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { + if (row.isHover + && re.frame + && (re.frame.x < row.hoverX + && (binderStruct.maxHeight * 20 - re.depth * 20 + 20) < row.hoverY + && re.frame.x + re.frame.width > row.hoverX + && binderStruct.maxHeight * 20 - re.depth * 20 + re.value * 20 + 20 > row.hoverY)) { binderStruct.hoverCpuFreqStruct = re; - } - if (!row.isHover) { + } else { binderStruct.hoverCpuFreqStruct = undefined; } binderStruct.draw(freqReq.context, re); @@ -57,9 +61,10 @@ export class binderStruct extends BaseStruct { static selectCpuFreqStruct: binderStruct | undefined; static maxHeight: number = 0; static hoverCycle: number = -1; + static isTableHover: boolean = false; cpu: number | undefined; value: number = 0; - cycle: number = 0; + cycle: number = -1; startNS: number | undefined; dur: number | undefined; //自补充,数据库没有返回 name: string | undefined; @@ -72,16 +77,16 @@ export class binderStruct extends BaseStruct { color = '#e86b6a'; } if (data.name === 'binder transaction async') { - color = '#36baa4'; + color = '#7da6f4'; } if (data.name === 'binder reply') { - color = '#8770d3'; + color = '#0cbdd4'; } if (data.name === 'binder async rcv') { - color = '#0cbdd4'; + color = '#8770d3'; } freqContext.fillStyle = color - if (data === binderStruct.hoverCpuFreqStruct || data === binderStruct.selectCpuFreqStruct || data.cycle === binderStruct.hoverCycle) { + if (data === binderStruct.hoverCpuFreqStruct || data === binderStruct.selectCpuFreqStruct || (data.cycle === binderStruct.hoverCycle && binderStruct.isTableHover)) { freqContext.globalAlpha = 1; freqContext.lineWidth = 1; freqContext.fillRect(data.frame.x, binderStruct.maxHeight * 20 - data.depth * 20 + 20, data.frame.width, data.value * 20);