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/SchedSwitchStruet.ts b/ide/src/trace/bean/SchedSwitchStruet.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f4131bbdb6fd7e96cf433e7c942cbb54fd2eb6c --- /dev/null +++ b/ide/src/trace/bean/SchedSwitchStruet.ts @@ -0,0 +1,49 @@ +/* + * 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 SchedSwitchStack { + count: number = 0; + cycleNum: number = 1; + duration: number | any; + isHover: boolean = false; + isSelected: boolean = false; + cycle: number = 0; + level: string = ''; + pid: number = -1; + process: string = ''; + state: string = ''; + status: boolean = false; + thread: string = ''; + tid: number = -1; + title: string = ''; + ts: string = ''; + cycleStartTime: number | any; + children: Array = []; +} +export class schedThreadInitData { + cycleEndTime: number = 0; + cycleStartTime: number = 0; + funId: number = 0; + name: string = '' + pid: number = -1; + runningCnt: number = 0; + state: string = ''; + tid: number = -1; + process: string = ''; + thread: string = ''; + dur: number = 0; + leftNS: number = 0 +} \ No newline at end of file diff --git a/ide/src/trace/component/chart/SpSegmentationChart.ts b/ide/src/trace/component/chart/SpSegmentationChart.ts index be34e9525a8d9b7df64e98a6c2f1b300a1f68a25..0256346b0c6263445b6cd972095f020a9a94886d 100644 --- a/ide/src/trace/component/chart/SpSegmentationChart.ts +++ b/ide/src/trace/component/chart/SpSegmentationChart.ts @@ -68,6 +68,7 @@ export class SegMenTaTion { value: v.count, startNS: v.startNS, cycle: v.cycle, + type } }) CpuFreqExtendStruct.maxValue = currentMaxValue; @@ -87,6 +88,7 @@ export class SegMenTaTion { value: v.count, startNS: Number(v.cycleStartTime) * 1000 * 1000, cycle: v.cycle, + type } }) CpuFreqExtendStruct.maxValue = currentMaxValue; @@ -350,7 +352,7 @@ export class SegMenTaTion { SegMenTaTion.trace?.displayTip( SegMenTaTion.schedRow!, CpuFreqExtendStruct.hoverCpuFreqStruct, - `${ColorUtils.formatNumberComma(CpuFreqExtendStruct.hoverCpuFreqStruct?.value!)} Hz·ms` + `${ColorUtils.formatNumberComma(CpuFreqExtendStruct.hoverCpuFreqStruct?.value!)} ` ); }; SegMenTaTion.schedRow.findHoverStruct = () => { diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index ff0f3522d37d393fa0100cac483dba817be51bd2..49b8ccb1bab67093be192ea15233d0426ecd7112 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -122,7 +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'; - +//@ts-ignore +import { TabPaneSchedSwitch } from '../sheet/schedswitch/TabPaneSchedSwitch.js'; export let tabConfig: any = { 'current-selection': { title: 'Current Selection', @@ -650,4 +651,9 @@ export let tabConfig: any = { type: TabPaneHiSysEventSummary, require: (param: SelectionParam) => param.hiSysEvents.length > 0, }, + 'tabpane-schedswitch': { + title: 'Sched Switch', + type: TabPaneSchedSwitch, + require: (param: SelectionParam) => param.threadIds.length > 0, + }, }; diff --git a/ide/src/trace/component/trace/sheet/schedswitch/TabPaneSchedSwitch.ts b/ide/src/trace/component/trace/sheet/schedswitch/TabPaneSchedSwitch.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad7e6440f1fa6b5936380474353d24dd6221bc26 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/schedswitch/TabPaneSchedSwitch.ts @@ -0,0 +1,739 @@ +/* + * 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 { LitButton } from '../../../../../base-ui/button/LitButton.js'; +import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table.js'; +import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection.js'; +import { querySchedThreadStates, querySingleCutData, queryLoopCutData } from '../../../../database/SqlLite.js'; +import { Utils } from '../../base/Utils.js'; +import { resizeObserver } from '../SheetUtils.js'; +import { LitChartColumn } from '../../../../../base-ui/chart/column/LitChartColumn.js'; +import { TableNoData } from '../../../schedulingAnalysis/TableNoData.js'; +import { getProbablyTime } from '../../../../database/logic-worker/ProcedureLogicWorkerCommon.js'; +import { SchedSwitchStack, schedThreadInitData } from '../../../../bean/SchedSwitchStruet.js'; +import { SegMenTaTion } from '../../../chart/SpSegmentationChart.js' + + +@element('tabpane-schedswitch') +export class TabPaneSchedSwitch extends BaseElement { + private schedSwitchTbl: LitTable | null | undefined; + private threadQueryDIV: Element | null | undefined; + private rightDIV: HTMLDivElement | null | undefined; + private threadIdInput: HTMLInputElement | null | undefined; + private funcNameInput: HTMLInputElement | null | undefined; + private singleBtn: LitButton | null | undefined; + private loopBtn: LitButton | null | undefined; + private cycleALeftInput: HTMLInputElement | null | undefined; + private cycleARightInput: HTMLInputElement | null | undefined; + private cycleBLeftInput: HTMLInputElement | null | undefined; + private cycleBRightInput: HTMLInputElement | null | undefined; + private queryButton: LitButton | null | undefined; + private threadFlag: string = ''; + private selectionParam: SelectionParam | undefined; + private canvansName: HTMLDivElement | null | undefined; + private singleSourceData: Array = []; + private loopSourceData: Array = []; + private chartTotal: LitChartColumn | null | undefined; + private histogramSource: Array = []; + private rangeA: object = {}; + private rangeB: object = {}; + private rangeTotal: object = {}; + private clickThreadChildren: Array = []; + private isThreadStatesData: boolean = false; + private clickHighlightCondition: string = ''; + + + set data(threadStatesParam: SelectionParam | any) { + if (this.selectionParam === threadStatesParam) return; + let tblVal: any = this.schedSwitchTbl; + let threadIdInput: any = this.threadIdInput; + let funcNameInput: any = this.funcNameInput; + this.schedSwitchTbl!.recycleDataSource = []; + this.queryButton!.style.pointerEvents = 'none'; + this.schedSwitchTbl!.loading = false; + this.isThreadStatesData = false; + tblVal.value = []; + threadIdInput.value = ''; + funcNameInput.value = ''; + funcNameInput.style.border = '1px solid rgb(151,151,151)'; + threadIdInput.style.border = '1px solid rgb(151,151,151)'; + this.selectionParam = threadStatesParam; + this.canvansName!.textContent = 'sched switch平均分布图'; + this.isQueryButtonClick(false); + this.isSingleButtonFn(false); + this.isLoopButtonFn(false); + this.isCanvansDisplayFn(false); + } + initElements(): void { + this.schedSwitchTbl = this.shadowRoot!.querySelector('#tb-running'); + this.threadQueryDIV = this.shadowRoot?.querySelector('#data-cut'); + this.rightDIV = this.shadowRoot?.querySelector('#right'); + this.canvansName = this.shadowRoot!.querySelector('.sched-subheading'); + this.chartTotal = this.shadowRoot!.querySelector('#chart_total'); + this.threadIdInput = this.shadowRoot?.getElementById('cut-threadid') as HTMLInputElement; + this.funcNameInput = this.shadowRoot?.querySelector('#cut-thread-func') as HTMLInputElement; + this.singleBtn = this.shadowRoot?.querySelector('.single-btn'); + this.loopBtn = this.shadowRoot?.querySelector('.loop-btn'); + this.cycleALeftInput = this.shadowRoot?.getElementById('leftA') as HTMLInputElement; + this.cycleARightInput = this.shadowRoot?.getElementById('rightA') as HTMLInputElement; + this.cycleBLeftInput = this.shadowRoot?.getElementById('leftB') as HTMLInputElement; + this.cycleBRightInput = this.shadowRoot?.getElementById('rightB') as HTMLInputElement; + this.queryButton = this.shadowRoot?.querySelector('.query-btn'); + this.singleBtn?.addEventListener('click', (e) => { this.queryCutInfoFn(this.singleBtn!.innerHTML) }); + this.loopBtn?.addEventListener('click', (e) => { this.queryCutInfoFn(this.loopBtn!.innerHTML) }); + this.queryButton!.addEventListener('click', (e) => { this.queryCycleRangeData() }); + this.schedSwitchTbl!.addEventListener('row-click', (evt: any) => { + let data = evt.detail.data as SchedSwitchStack; + if (data.level == 'process') { + this.isCanvansDisplayFn(false); + this.threadFlag = ''; + } else if (data.level == 'thread') { + this.cycleALeftInput!.value = ''; + this.cycleARightInput!.value = ''; + this.cycleBLeftInput!.value = ''; + this.cycleBRightInput!.value = ''; + this.histogramSource = []; + this.clickThreadChildren = data.children; + this.queryButton!.style.pointerEvents = 'none'; + this.isCanvansDisplayFn(true); + this.isQueryButtonClick(false); + this.threadFlag = 'thread'; + this.clickHighlightCondition = `${data.process} - ${data.pid} - ${data.thread} - ${data.tid}`; + this.rangeTotal = { + count: evt.detail.count, + cycleNum: evt.detail.cycleNum, + average: evt.detail.cycleNum ? Math.ceil(evt.detail.count / evt.detail.cycleNum) : 0, + size: 'Total', + isHover: false, + color: '#2f72f8' + }; + this.histogramSource.push(this.rangeTotal); + data.isSelected = true; + this.schedSwitchTbl!.clearAllSelection(data); + this.schedSwitchTbl!.setCurrentSelection(data); + SegMenTaTion.setChartData('SCHED-SWITCH', evt.detail.children); + this.queryHistogramData() + } else if (data.level == 'cycle') { + if (this.threadFlag == 'thread') { + this.isCanvansDisplayFn(true); + } + if (this.clickHighlightCondition == `${data.process} - ${data.pid} - ${data.thread} - ${data.tid}`) { + data.isSelected = true; + this.schedSwitchTbl!.clearAllSelection(data); + this.schedSwitchTbl!.setCurrentSelection(data); + SegMenTaTion.tabHover('SCHED-SWITCH', true, data!.cycle); + } + } + }) + this.cycleALeftInput!.addEventListener('input', (evt: any) => { + this.checkInputRangeFn(this.cycleALeftInput, this.cycleARightInput, this.cycleBLeftInput, this.cycleBRightInput, this.cycleALeftInput!.value, this.cycleARightInput!.value) + }) + this.cycleARightInput!.addEventListener('input', (evt: any) => { + this.checkInputRangeFn(this.cycleARightInput, this.cycleALeftInput, this.cycleBLeftInput, this.cycleBRightInput, this.cycleALeftInput!.value, this.cycleARightInput!.value) + }) + this.cycleBLeftInput!.addEventListener('input', (evt: any) => { + this.checkInputRangeFn(this.cycleBLeftInput, this.cycleBRightInput, this.cycleALeftInput, this.cycleARightInput, this.cycleBLeftInput!.value, this.cycleBRightInput!.value) + }) + this.cycleBRightInput!.addEventListener('input', (evt: any) => { + this.checkInputRangeFn(this.cycleBRightInput, this.cycleBLeftInput, this.cycleALeftInput, this.cycleARightInput, this.cycleBLeftInput!.value, this.cycleBRightInput!.value) + }) + } + + checkInputRangeFn(firstInput: any, secondInput: any, thirdInput: any, fourInput: any, lVal: string, rVal: string): void { + let leftVal = Number(lVal); + let rightVal = Number(rVal); + if (firstInput!.value != '' && secondInput!.value != '') { + if (firstInput!.value != '' && secondInput!.value != '') { + if (leftVal >= rightVal) { + firstInput!.style.color = 'red'; + this.queryButton!.style.pointerEvents = 'none'; + this.isQueryButtonClick(false); + } else if (leftVal < rightVal) { + firstInput!.style.color = 'black'; + secondInput!.style.color = 'black'; + this.queryButton!.style.pointerEvents = 'auto'; + this.isQueryButtonClick(true); + } + } + } else if ((firstInput.value == '' && secondInput!.value != '') || (firstInput!.value != '' && secondInput!.value == '')) { + this.queryButton!.style.pointerEvents = 'none'; + this.isQueryButtonClick(false); + } else if (firstInput!.value == '' && secondInput!.value == '' && thirdInput!.value != '' && fourInput!.value != '') { + this.queryButton!.style.pointerEvents = 'auto'; + this.isQueryButtonClick(true); + } + } + + async initThreadStateData(threadParam: SelectionParam | null | undefined): Promise { + let leftStartNs = threadParam!.leftNs + threadParam!.recordStartNs; + let rightEndNs = threadParam!.rightNs + threadParam!.recordStartNs; + let res = await querySchedThreadStates(threadParam!.threadIds, leftStartNs, rightEndNs); + if (res.length == 0) { + this.schedSwitchTbl!.recycleDataSource = []; + this.schedSwitchTbl!.loading = false; + this.clickTreeTitleFn(this.schedSwitchTbl!.recycleDataSource); + return + } + this.singleSourceData = JSON.parse(JSON.stringify(res)); + this.loopSourceData = JSON.parse(JSON.stringify(res)); + this.isThreadStatesData = true; + } + + async queryCutInfoFn(btnHtml: string): Promise { + let threadId = this.threadIdInput!.value.trim(); + let threadFunName = this.funcNameInput!.value.trim(); + let leftStartNs = this.selectionParam!.leftNs + this.selectionParam!.recordStartNs; + let rightEndNs = this.selectionParam!.rightNs + this.selectionParam!.recordStartNs; + let cutData: Array = []; + let tblVal: any = this.schedSwitchTbl; + this.threadFlag = ''; + if (threadId != "" && threadFunName != "") { + this.threadIdInput!.style.border = '1px solid rgb(151,151,151)'; + this.funcNameInput!.style.border = '1px solid rgb(151,151,151)'; + tblVal!.value = []; + this.isCanvansDisplayFn(false); + this.schedSwitchTbl!.loading = true; + if (!this.isThreadStatesData) this.initThreadStateData(this.selectionParam); + if (btnHtml == 'Single') { + this.isSingleButtonFn(true); + this.isLoopButtonFn(false); + let res = await querySingleCutData(threadFunName, threadId, leftStartNs, rightEndNs); + if (res.length == 0) { + this.schedSwitchTbl!.recycleDataSource = []; + this.schedSwitchTbl!.loading = false; + this.clickTreeTitleFn(this.schedSwitchTbl!.recycleDataSource); + return + }; + for (let idx = 0; idx < res.length; idx++) { + for (let i = 0; i < this.singleSourceData.length; i++) { + let singleItem = this.singleSourceData[i] + if (!(singleItem.endTs < res[idx].cycleStartTime || singleItem.ts > res[idx].cycleEndTime)) { + let info = { + pid: singleItem.pid, + tid: singleItem.tid, + state: singleItem.state, + cycleStartTime: res[idx].cycleStartTime, + cycleEndTime: res[idx].cycleEndTime, + name: res[idx].name, + funId: res[idx].id, + runningCnt: singleItem.state == 'Running' ? 1 : 0, + } + cutData.push(info) + } + } + } + }; + if (btnHtml == 'Loop') { + this.isLoopButtonFn(true); + this.isSingleButtonFn(false); + this.schedSwitchTbl!.loading = true; + let res = await queryLoopCutData(threadFunName, threadId, leftStartNs, rightEndNs); + if (res.length == 0) { + this.schedSwitchTbl!.recycleDataSource = []; + this.schedSwitchTbl!.loading = false; + this.clickTreeTitleFn(this.schedSwitchTbl!.recycleDataSource); + return + }; + for (let idx = 0; idx < res.length; idx++) { + if (idx + 1 == res.length) { + res.splice(idx, 1); + } else { + res[idx].cycleEndTime = res[idx + 1].cycleStartTime; + for (let i = 0; i < this.loopSourceData.length; i++) { + let loopItem = this.loopSourceData[i] + if (!(loopItem.endTs < res[idx].cycleStartTime || loopItem.ts > res[idx].cycleEndTime)) { + let info = { + pid: loopItem.pid, + tid: loopItem.tid, + state: loopItem.state, + cycleStartTime: res[idx].cycleStartTime, + cycleEndTime: res[idx + 1].cycleStartTime, + name: res[idx].name, + funId: res[idx].id, + runningCnt: loopItem.state == 'Running' ? 1 : 0, + } + cutData.push(info) + } + } + } + } + }; + this.handleSchedThreadData(cutData) + } else { + if (threadId == "") { + this.threadIdInput!.style.border = '2px solid rgb(255,0,0)'; + this.threadIdInput!.setAttribute('placeholder', 'Please input thread id'); + } else { + this.threadIdInput!.style.border = '1px solid rgb(151,151,151)'; + }; + if (threadFunName == '') { + this.funcNameInput!.style.border = '2px solid rgb(255,0,0)'; + this.funcNameInput!.setAttribute('placeholder', 'Please input function name'); + } else { + this.funcNameInput!.style.border = '1px solid rgb(151,151,151)'; + } + } + } + + handleSchedThreadData(result: Array): void { + let resultData: Array = []; + if (result != null && result.length > 0) { + for (let e of result) { + if (this.selectionParam!.processIds.includes(e.pid)) { + let process = Utils.PROCESS_MAP.get(e.pid); + let thread = Utils.THREAD_MAP.get(e.tid); + e.process = process == null || process.length == 0 ? '[NULL]' : process; + e.thread = thread == null || thread.length == 0 ? '[NULL]' : thread; + e.dur = e.cycleEndTime - e.cycleStartTime; + e.leftNS = e.cycleStartTime - this.selectionParam!.recordStartNs; + resultData.push(e); + } + } + this.translateIntoTreeData(resultData); + } + } + + translateIntoTreeData(data: Array): void { + let group: any = {}; + if (data != null && data.length > 0) { + data.forEach((slice: any) => { + let item = { + title: `${slice.thread}`, + count: slice.runningCnt, + cycleNum: 1, + state: slice.state, + tid: slice.tid, + pid: slice.pid, + thread: slice.thread, + process: slice.process, + cycleStartTime: slice.leftNS, + duration: slice.dur, + level: 'cycle', + }; + if (group[`${slice.pid}`]) { + let process = group[`${slice.pid}`]; + process.count += slice.runningCnt; + let thread = process.children.find((child: any) => child.title === `${slice.thread}` + `[${slice.tid}]`); + if (thread) { + thread.count += slice.runningCnt; + let cycle = thread.children.find((child: any) => child.cycleStartTime === slice.leftNS); + if (cycle) { + cycle.count += slice.runningCnt; + } else { + thread.cycleNum += 1; + process.cycleNum += 1; + thread.duration += slice.dur; + process.duration += slice.dur; + thread.children.push(item); + } + } else { + process.cycleNum += 1; + process.duration += slice.dur; + process.children.push({ + title: `${slice.thread}` + `[${slice.tid}]`, + count: slice.runningCnt, + cycleNum: 1, + pid: slice.pid, + tid: slice.tid, + thread: slice.thread, + process: slice.process, + duration: slice.dur, + level: 'thread', + cycleStartTime: '', + children: [item] + }); + } + } else { + group[`${slice.pid}`] = { + title: `${slice.process}` + `[${slice.pid}]`, + count: slice.runningCnt, + cycleNum: 1, + tid: slice.tid, + pid: slice.pid, + thread: slice.thread, + process: slice.process, + duration: slice.dur, + level: 'process', + cycleStartTime: '', + children: [ + { + title: `${slice.thread}` + `[${slice.tid}]`, + count: slice.runningCnt, + cycleNum: 1, + tid: slice.tid, + pid: slice.pid, + thread: slice.thread, + process: slice.process, + duration: slice.dur, + level: 'thread', + cycleStartTime: '', + children: [item] + }, + ], + }; + } + }); + group = Object.values(group); + for (let i = 0; i < group.length; i++) { + this.addCycleNumber([group[i]]); + } + this.schedSwitchTbl!.recycleDataSource = group; + this.schedSwitchTbl!.loading = false; + this.clickTreeTitleFn(this.schedSwitchTbl!.recycleDataSource); + } + } + addCycleNumber(groupItem: Array): void { + let flagNumber = 0; + for (let idx = 0; idx < groupItem.length; idx++) { + groupItem[idx].duration = (groupItem[idx].duration / 1000000.0).toFixed(3); + if (!(groupItem[idx].children)) { + flagNumber += 1; + groupItem[idx].cycle = flagNumber; + groupItem[idx].title = `cycle ${flagNumber}-` + groupItem[idx].title; + groupItem[idx].cycleStartTime = (groupItem[idx].cycleStartTime / 1000000.0).toFixed(3); + } else { + this.addCycleNumber(groupItem[idx].children) + } + } + } + + clickTreeTitleFn(data: Array): void { + let labelList = this.schedSwitchTbl!.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label'); + if (labelList) { + for (let i = 0; i < labelList.length; i++) { + let lable = labelList[i].innerHTML; + labelList[i].addEventListener('click', (e) => { + if (lable.includes('Process') && i == 0) { + this.schedSwitchTbl!.setStatus(data, false); + this.schedSwitchTbl!.recycleDs = this.schedSwitchTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (lable.includes('Thread') && i == 1) { + for (let item of data) { + item.status = true; + if (item.children != undefined && item.children.length > 0) { + this.schedSwitchTbl!.setStatus(item.children, false); + } + } + this.schedSwitchTbl!.recycleDs = this.schedSwitchTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (lable.includes('Cycle') && i == 2) { + this.schedSwitchTbl!.setStatus(data, true); + this.schedSwitchTbl!.recycleDs = this.schedSwitchTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand) + } + }) + + } + } + + } + + queryCycleRangeData(): void { + let cycleALeft = this.cycleALeftInput!.value.trim(); + let cycleARight = this.cycleARightInput!.value.trim(); + let cycleBLeft = this.cycleBLeftInput!.value.trim(); + let cycleBRight = this.cycleBRightInput!.value.trim(); + this.histogramSource = []; + this.histogramSource.push(this.rangeTotal) + if (cycleALeft != '' && cycleARight != '' && cycleALeft != cycleARight) { + let countA = 0; + let rangeFilterA = this.clickThreadChildren.filter((it: any) => Number(it.duration) >= Number(cycleALeft) && Number(it.duration) < Number(cycleARight)); + rangeFilterA.forEach((item: any) => { + countA += item.count + }) + this.rangeA = { + count: countA, + cycleNum: rangeFilterA.length, + average: rangeFilterA.length ? Math.ceil(countA / rangeFilterA.length) : 0, + size: 'Cycle A', + isHover: false, + color: '#ffab67' + } + this.histogramSource.push(this.rangeA); + } + + if (cycleBLeft != '' && cycleBRight != '' && cycleBLeft != cycleBRight) { + let countB = 0; + let rangeFilterB = this.clickThreadChildren.filter((it: any) => Number(it.duration) >= Number(cycleBLeft) && Number(it.duration) < Number(cycleBRight)) + rangeFilterB.forEach((item: any) => { + countB += item.count + }) + this.rangeB = { + count: countB, + cycleNum: rangeFilterB.length, + average: rangeFilterB.length ? Math.ceil(countB / rangeFilterB.length) : 0, + size: 'Cycle B', + isHover: false, + color: '#a285d2' + } + this.histogramSource.push(this.rangeB) + } + this.queryHistogramData(); + } + + queryHistogramData(): void { + let source = []; + source = this.histogramSource.map((it: any, index: number) => { + let data: any = { + cycle: it.size, + average: it.average, + visible: 1, + color: it.color, + }; + return data; + }); + this.chartTotal!.config = { + data: source, + appendPadding: 10, + xField: 'cycle', + yField: 'average', + notSort: true, + removeUnit: true, + seriesField: '', + color: (a) => { + if (a.cycle === 'Total') { + return '#2f72f8'; + } else if (a.cycle === 'Cycle A') { + return '#ffab67'; + } else if (a.cycle === 'Cycle B') { + return '#a285d2'; + } else { + return '#0a59f7'; + } + }, + tip: (a) => { + if (a && a[0]) { + let tip = ''; + for (let obj of a) { + tip = + `${tip} +
+
+
${obj.xLabel}:${obj.obj.average}
+
+ `; + } + return tip; + } else { + return ''; + } + }, + label: null, + }; + } + + isCanvansDisplayFn(flag: boolean): void { + if (!flag) { + this.setAttribute('isCanvansDisplay', '') + } else { + this.removeAttribute('isCanvansDisplay') + } + } + + isSingleButtonFn(flag: boolean): void { + if (flag) { + this.setAttribute('isSingleButton', '') + } else { + this.removeAttribute('isSingleButton') + } + } + + isLoopButtonFn(flag: boolean): void { + if (flag) { + this.setAttribute('isLoopButton', '') + } else { + this.removeAttribute('isLoopButton') + } + } + + isQueryButtonClick(flag: boolean): void { + if (flag) { + this.setAttribute('isQueryButton', '') + } else { + this.removeAttribute('isQueryButton') + } + } + + connectedCallback() { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.schedSwitchTbl!); + } + initHtml(): string { + return ` + +
+ + +
+ + +
+
+
+
+ + + + + + + + + + +
+ + +
+ + ` + } +} diff --git a/ide/src/trace/database/SqlLite.ts b/ide/src/trace/database/SqlLite.ts index 772df4a2200f1e96fe702efec4071ed619209d4b..b825c04cd3129840027d855c771c994569df0571 100644 --- a/ide/src/trace/database/SqlLite.ts +++ b/ide/src/trace/database/SqlLite.ts @@ -5816,3 +5816,87 @@ export const queryHiSysEventData = (): Promise> => `, { $search: funcName } ); + +export const querySchedThreadStates = (tIds: Array, leftStartNs: number, rightEndNs: number): Promise> => + query( + 'getTabThreadStates', + ` + select + B.id, + B.pid, + B.tid, + B.state, + B.type, + B.dur, + B.ts, + B.dur + B.ts as endTs + from + thread_state AS B + where + B.tid in (${tIds.join(',')}) + and + not ((B.ts + ifnull(B.dur,0) < $leftStartNs) or (B.ts > $rightEndNs)) + order by + B.pid; + `, + { $leftStartNs: leftStartNs, $rightEndNs: rightEndNs } + ); + +export const querySingleCutData = (funcName: string, tIds: string, leftStartNs: number, rightEndNs: number): Promise> => + query( + 'querySingleCutData', + ` + select + c.id, + c.name, + c.ts as cycleStartTime, + c.ts + c.dur as cycleEndTime, + c.depth, + t.tid, + p.pid, + c.dur + from + callstack c + left join + thread t on c.callid = t.id + left join + process p on t.ipid = p.id + left join + trace_range r + where + c.name = '${funcName}' + and + t.tid = '${tIds}' + and + not ((c.ts < $leftStartNs) or (c.ts + ifnull(c.dur, 0) > $rightEndNs)) + `, + {$leftStartNs: leftStartNs, $rightEndNs: rightEndNs} + ) + +export const queryLoopCutData = ( funcName: string, tIds: string, leftStartNs: number, rightEndNs: number): Promise> => +query ( + 'queryLoopCutData', + ` + select + c.id, + c.name, + c.ts as cycleStartTime, + c.depth, + t.tid, + p.pid + from callstack c + 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 ((c.ts < $leftStartNs) or (c.ts > $rightEndNs)) + order by + c.ts + `, + { $leftStartNs: leftStartNs, $rightEndNs: rightEndNs } +) \ No newline at end of file diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerFreqExtend.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerFreqExtend.ts index 93759b2f7646ddb36dbe1d0c5ab8557acafbef31..93758690b5dd4b587935b90c612c3c09ac1bc743 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorkerFreqExtend.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerFreqExtend.ts @@ -41,6 +41,7 @@ export class FreqExtendRender extends Render { if (row.isHover) { CpuFreqExtendStruct.cycle = -1; + CpuFreqExtendStruct.isTabHover = false; } freqReq.context.beginPath(); for (let re of freqFilter) { @@ -66,16 +67,18 @@ export class CpuFreqExtendStruct extends BaseStruct { startNS: number | undefined; dur: number | undefined; //自补充,数据库没有返回 cycle: number | undefined; + type: string | undefined; static draw(freqContext: CanvasRenderingContext2D, data: CpuFreqExtendStruct) { if (data.frame) { let width = data.frame.width || 0; let index = data.cpu || 0; index += 2; - freqContext.fillStyle = ColorUtils.colorForTid(index); - freqContext.strokeStyle = ColorUtils.colorForTid(index); - if (data === CpuFreqExtendStruct.hoverCpuFreqStruct - || data === CpuFreqExtendStruct.selectCpuFreqStruct + let color = ColorUtils.colorForTid(index) + freqContext.fillStyle = color; + freqContext.strokeStyle = color; + if (data === CpuFreqExtendStruct.hoverCpuFreqStruct + || data === CpuFreqExtendStruct.selectCpuFreqStruct || data === CpuFreqExtendStruct.selectCpuFreqStruct || (data.cycle === CpuFreqExtendStruct.cycle && CpuFreqExtendStruct.cycle !== -1)) { @@ -83,6 +86,11 @@ export class CpuFreqExtendStruct extends BaseStruct { freqContext.strokeStyle = '#ff0000'; freqContext.lineWidth = 3; freqContext.globalAlpha = 0.6; + if (data.type === 'SCHED-SWITCH' || data.type === 'GPU-FREQ') { + freqContext.globalAlpha = 1; + freqContext.fillStyle = color; + freqContext.strokeStyle = color; + } let drawHeight: number = Math.floor( ((data.value || 0) * (data.frame.height || 0) * 1.0) / CpuFreqExtendStruct.maxValue );