diff --git a/ide/src/trace/SpApplication.ts b/ide/src/trace/SpApplication.ts index 0c2de71201103dcad3a65ae57316a0621893f4cd..512d7216c2859a3d583a6c126f4462f8f4add97b 100644 --- a/ide/src/trace/SpApplication.ts +++ b/ide/src/trace/SpApplication.ts @@ -1521,12 +1521,12 @@ export class SpApplication extends BaseElement { }, }, { - title: 'Keyboard shortcuts', + title: 'Keyboard Shortcuts', icon: 'smart-help', clickHandler: function (item: MenuItem) { SpStatisticsHttpUtil.addOrdinaryVisitAction({ - event: 'Keyboard shortcuts', - action: 'Keyboard shortcuts', + event: 'Keyboard Shortcuts', + action: 'Keyboard Shortcuts', }); that.search = false; showContent(SpKeyboard); @@ -1981,14 +1981,14 @@ export class SpApplication extends BaseElement { }, }, { - title: 'Keyboard shortcuts', + title: 'Keyboard Shortcuts', icon: 'smart-help', clickHandler: function (item: MenuItem) { that.search = false; showContent(SpKeyboard); SpStatisticsHttpUtil.addOrdinaryVisitAction({ - event: 'Keyboard shortcuts', - action: 'Keyboard shortcuts', + event: 'Keyboard Shortcuts', + action: 'Keyboard Shortcuts', }); }, }, diff --git a/ide/src/trace/bean/SearchFuncBean.ts b/ide/src/trace/bean/SearchFuncBean.ts index 40bfb5baa53801a350efbc909a3b773a0031dc1f..365608b974352babc83661cfbc4353f57a011c35 100644 --- a/ide/src/trace/bean/SearchFuncBean.ts +++ b/ide/src/trace/bean/SearchFuncBean.ts @@ -21,6 +21,7 @@ export class SearchFuncBean { startTime: number | undefined; // 9729867000 tid: number | undefined; // pid: number | undefined; // 2785 + endTime: number | undefined; // 9729867000 type: string | undefined; } diff --git a/ide/src/trace/component/SpKeyboard.ts b/ide/src/trace/component/SpKeyboard.ts index dac46503215fc83a53861e3362e26e83d611f3a7..f939b8e47223d322a4d9f37c13a5656f04e015a6 100644 --- a/ide/src/trace/component/SpKeyboard.ts +++ b/ide/src/trace/component/SpKeyboard.ts @@ -43,7 +43,7 @@ export class SpKeyboard extends BaseElement {
-

SmartPerf help

+

SmartPerf Help

@@ -172,7 +172,7 @@ export class SpKeyboard extends BaseElement { .body{ width: 50%; background-color: #fff; - padding: 30px; + padding: 0 30px 30px; z-index: 9000; max-height: 600px; overflow-y: scroll; @@ -182,10 +182,11 @@ export class SpKeyboard extends BaseElement { position:absolute; } header { - position: relative; - width: 100%; - height: 31px; - line-height: 31px; + position: fixed; + width: 50%; + height: 50px; + line-height: 50px; + background-color: #fff; } .close-icon{ cursor: pointer; diff --git a/ide/src/trace/component/SpRecordTrace.ts b/ide/src/trace/component/SpRecordTrace.ts index f1b3d6a4ea5193d6051a57e2cb3f67c201a9492d..6613671509ba4780c60952a67ea50badfcd6ec19 100644 --- a/ide/src/trace/component/SpRecordTrace.ts +++ b/ide/src/trace/component/SpRecordTrace.ts @@ -837,9 +837,12 @@ export class SpRecordTrace extends BaseElement { SpRecordTrace.supportVersions.forEach((supportVersion) => { let option = document.createElement('option'); option.className = 'select'; + option.selected = supportVersion === '4.0+'? true:false; option.textContent = `OpenHarmony-${supportVersion}`; option.setAttribute('device-version', supportVersion); this.deviceVersion!.append(option); + SpRecordTrace.selectVersion = '4.0+' + this.nativeMemoryHideBySelectVersion(); }); } diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index eeef49272035fbe8f43387d936d41cdb66b167e4..2e8751161688c6daedc54c439199cba62b039433 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -109,6 +109,8 @@ import { type HiSysEventStruct } from '../database/ui-worker/ProcedureWorkerHiSy import { InitAnalysis } from '../database/logic-worker/ProcedureLogicWorkerCommon'; import { type SpKeyboard } from '../component/SpKeyboard'; import { drawVSync, enableVSync, resetVSync } from './chart/VSync'; +import { LtpoStruct } from '../database/ui-worker/ProcedureWorkerLTPO'; +import { HitchTimeStruct } from '../database/ui-worker/ProcedureWorkerHitchTime' function dpr() { return window.devicePixelRatio || 1; @@ -2081,6 +2083,12 @@ export class SpSystemTrace extends BaseElement { (AppStartupStruct.selectStartupStruct.startTs || 0) + (AppStartupStruct.selectStartupStruct.dur || 0), shiftKey ); + }else if (AllAppStartupStruct.selectStartupStruct) { + this.slicestime = this.timerShaftEL?.setSlicesMark( + AllAppStartupStruct.selectStartupStruct.startTs || 0, + (AllAppStartupStruct.selectStartupStruct.startTs || 0) + (AllAppStartupStruct.selectStartupStruct.dur || 0), + shiftKey + ); } else if (SoStruct.selectSoStruct) { this.slicestime = this.timerShaftEL?.setSlicesMark( SoStruct.selectSoStruct.startTs || 0, @@ -2479,6 +2487,9 @@ export class SpSystemTrace extends BaseElement { JsCpuProfilerStruct.selectJsCpuProfilerStruct = undefined; SnapshotStruct.selectSnapshotStruct = undefined; HiPerfCallChartStruct.selectStruct = undefined; + AllAppStartupStruct.selectStartupStruct = undefined; + LtpoStruct.selectLtpoStruct = undefined; + HitchTimeStruct.selectHitchTimeStruct = undefined; } isWASDKeyPress() { diff --git a/ide/src/trace/component/chart/SpAllAppStartups.ts b/ide/src/trace/component/chart/SpAllAppStartups.ts index 0046a27e09fe3a7ea5030489bf8f3fb14995fb0f..60141f681779804d84b189f35be11072ca9b527c 100644 --- a/ide/src/trace/component/chart/SpAllAppStartups.ts +++ b/ide/src/trace/component/chart/SpAllAppStartups.ts @@ -17,117 +17,121 @@ import { SpSystemTrace } from '../SpSystemTrace'; import { TraceRow } from '../trace/base/TraceRow'; import { renders } from '../../database/ui-worker/ProcedureWorker'; import { CpuFreqStruct } from '../../database/ui-worker/ProcedureWorkerFreq'; -import { queryAppStartupProcessIds, queryProcessStartup, querySingleAppStartupsName } from '../../database/SqlLite'; +import { + queryAppStartupProcessIds, + queryProcessStartup, + querySingleAppStartupsName, +} from '../../database/SqlLite'; import { FlagsConfig } from '../SpFlags'; import { AllAppStartupStruct, AllAppStartupRender } from '../../database/ui-worker/ProcedureWorkerAllAppStartup'; export class SpAllAppStartupsChart { - private readonly trace: SpSystemTrace | undefined; - static APP_STARTUP_PID_ARR: Array = []; - static jsonRow: TraceRow | undefined; - static trace: SpSystemTrace; - static AllAppStartupsNameArr: any[] = []; - static allAppStartupsAva: number[] = []; + private readonly trace: SpSystemTrace | undefined; + static APP_STARTUP_PID_ARR: Array = []; + static jsonRow: TraceRow | undefined; + static trace: SpSystemTrace; + static AllAppStartupsNameArr: any[] = []; + static allAppStartupsAva: number[] = []; - constructor(trace: SpSystemTrace) { - SpAllAppStartupsChart.trace = trace; - } - - async init() { - SpAllAppStartupsChart.APP_STARTUP_PID_ARR = []; - let appStartUpPids = await queryAppStartupProcessIds(); - appStartUpPids.forEach((it) => SpAllAppStartupsChart.APP_STARTUP_PID_ARR.push(it.pid)); - SpAllAppStartupsChart.AllAppStartupsNameArr = []; - SpAllAppStartupsChart.allAppStartupsAva = []; - for (let i = 0; i < SpAllAppStartupsChart.APP_STARTUP_PID_ARR.length; i++) { - let tmpSingleApp: any[] = await queryProcessStartup(SpAllAppStartupsChart.APP_STARTUP_PID_ARR[i]!); - if (tmpSingleApp.length == 8) { - let avilSingleName = await querySingleAppStartupsName(SpAllAppStartupsChart.APP_STARTUP_PID_ARR[i]!); - SpAllAppStartupsChart.allAppStartupsAva.push(SpAllAppStartupsChart.APP_STARTUP_PID_ARR[i]); - SpAllAppStartupsChart.AllAppStartupsNameArr.push(avilSingleName![0].name); - } + constructor(trace: SpSystemTrace) { + SpAllAppStartupsChart.trace = trace; } - let loadAppStartup: boolean = FlagsConfig.getFlagsConfigEnableStatus('AppStartup'); - if (loadAppStartup && SpAllAppStartupsChart.allAppStartupsAva.length) await this.initFolder(); - } - - async initFolder() { - let row: TraceRow = TraceRow.skeleton(); - row.setAttribute('hasStartup', 'true'); - row.rowId = `all-app-start-${SpAllAppStartupsChart.APP_STARTUP_PID_ARR![0]}`; - row.index = 0; - row.rowType = TraceRow.ROW_TYPE_ALL_APPSTARTUPS; - row.rowParentId = ''; - row.folder = false; - row.style.height = '40px'; - row.name = `All App Startups`; - row.selectChangeHandler = SpAllAppStartupsChart.trace.selectChangeHandler; - row.favoriteChangeHandler = SpAllAppStartupsChart.trace.favoriteChangeHandler; - row.supplier = async (): Promise> => { - let sendRes: AllAppStartupStruct[] | PromiseLike = []; - for (let i = 0; i < SpAllAppStartupsChart.allAppStartupsAva.length; i++) { - let tmpResArr = await queryProcessStartup(SpAllAppStartupsChart.allAppStartupsAva[i]); - let maxStartTs: number | undefined = tmpResArr[0].startTs; - let minStartTs: number | undefined = tmpResArr[0].startTs; - let singleDur = tmpResArr[0].dur; - let endTs: number | undefined = tmpResArr[0].startTs; - if (tmpResArr.length > 1) { - for (let j = 0; j < tmpResArr.length; j++) { - if (Number(tmpResArr[j].startTs) > Number(maxStartTs)) { - maxStartTs = tmpResArr[j].startTs; - } else if (Number(tmpResArr[j].startTs) < Number(minStartTs)) { - minStartTs = tmpResArr[j].startTs; + async init() { + SpAllAppStartupsChart.APP_STARTUP_PID_ARR = []; + let appStartUpPids = await queryAppStartupProcessIds(); + appStartUpPids.forEach((it) => SpAllAppStartupsChart.APP_STARTUP_PID_ARR.push(it.pid)); + SpAllAppStartupsChart.AllAppStartupsNameArr = []; + SpAllAppStartupsChart.allAppStartupsAva = []; + for (let i = 0; i < SpAllAppStartupsChart.APP_STARTUP_PID_ARR.length; i++) { + let tmpSingleApp: any[] = await queryProcessStartup(SpAllAppStartupsChart.APP_STARTUP_PID_ARR[i]!); + if (tmpSingleApp.length === 8) { + let avilSingleName = await querySingleAppStartupsName(SpAllAppStartupsChart.APP_STARTUP_PID_ARR[i]!); + SpAllAppStartupsChart.allAppStartupsAva.push(SpAllAppStartupsChart.APP_STARTUP_PID_ARR[i]); + SpAllAppStartupsChart.AllAppStartupsNameArr.push(avilSingleName![0].name); } - } - tmpResArr.forEach((item) => { - if (item.startTs == maxStartTs) { - endTs = Number(item.startTs) + Number(item.dur); - singleDur = Number(endTs) - Number(minStartTs); + } + let loadAppStartup: boolean = FlagsConfig.getFlagsConfigEnableStatus('AppStartup'); + if (loadAppStartup && SpAllAppStartupsChart.allAppStartupsAva.length) await this.initFolder(); + } + + async initFolder() { + let row: TraceRow = TraceRow.skeleton(); + row.setAttribute('hasStartup', 'true'); + row.rowId = `all-app-start-${SpAllAppStartupsChart.APP_STARTUP_PID_ARR![0]}`; + row.index = 0; + row.rowType = TraceRow.ROW_TYPE_ALL_APPSTARTUPS; + row.rowParentId = ''; + row.folder = false; + row.style.height = '40px'; + row.name = `All App Startups`; + row.addTemplateTypes('AppStartup'); + row.selectChangeHandler = SpAllAppStartupsChart.trace.selectChangeHandler; + row.favoriteChangeHandler = SpAllAppStartupsChart.trace.favoriteChangeHandler; + row.supplier = async (): Promise> => { + let sendRes: AllAppStartupStruct[] | PromiseLike = []; + for (let i = 0; i < SpAllAppStartupsChart.allAppStartupsAva.length; i++) { + let tmpResArr = await queryProcessStartup(SpAllAppStartupsChart.allAppStartupsAva[i]); + let maxStartTs: number | undefined = tmpResArr[0].startTs; + let minStartTs: number | undefined = tmpResArr[0].startTs; + let singleDur = tmpResArr[0].dur; + let endTs: number | undefined = tmpResArr[0].startTs; + if (tmpResArr.length > 1) { + for (let j = 0; j < tmpResArr.length; j++) { + if (Number(tmpResArr[j].startTs) > Number(maxStartTs)) { + maxStartTs = tmpResArr[j].startTs; + } else if (Number(tmpResArr[j].startTs) < Number(minStartTs)) { + minStartTs = tmpResArr[j].startTs; + } + } + tmpResArr.forEach((item) => { + if (item.startTs === maxStartTs) { + endTs = Number(item.startTs) + Number(item.dur); + singleDur = Number(endTs) - Number(minStartTs); + } + }) + } else if (tmpResArr.length === 1) { + minStartTs = tmpResArr[0].startTs; + singleDur = tmpResArr[0].dur; + } + sendRes.push( + { + dur: singleDur, + value: undefined, + startTs: minStartTs, + pid: SpAllAppStartupsChart.allAppStartupsAva[i], + process: undefined, + itid: undefined, + endItid: undefined, + tid: SpAllAppStartupsChart.allAppStartupsAva[i], + startName: undefined, + stepName: SpAllAppStartupsChart.AllAppStartupsNameArr[i], + translateY: undefined, + frame: undefined, + isHover: false + } + ) } - }); - } else if (tmpResArr.length === 1) { - minStartTs = tmpResArr[0].startTs; - singleDur = tmpResArr[0].dur; + return sendRes } - sendRes.push({ - dur: singleDur, - value: undefined, - startTs: minStartTs, - pid: SpAllAppStartupsChart.allAppStartupsAva[i], - process: undefined, - itid: undefined, - endItid: undefined, - tid: SpAllAppStartupsChart.allAppStartupsAva[i], - startName: undefined, - stepName: SpAllAppStartupsChart.AllAppStartupsNameArr[i], - translateY: undefined, - frame: undefined, - isHover: false, - }); - } - return sendRes; - }; - row.onThreadHandler = (useCache): void => { - let context: CanvasRenderingContext2D; - if (row.currentContext) { - context = row.currentContext; - } else { - context = row.collect - ? SpAllAppStartupsChart.trace.canvasFavoritePanelCtx! - : SpAllAppStartupsChart.trace.canvasPanelCtx!; - } - row.canvasSave(context); - (renders['all-app-start-up'] as AllAppStartupRender).renderMainThread( - { - appStartupContext: context, - useCache: useCache, - type: `app-startup ${row.rowId}`, - }, - row - ); - row.canvasRestore(context); - }; - SpAllAppStartupsChart.trace.rowsEL?.appendChild(row); - } -} + row.onThreadHandler = (useCache): void => { + let context: CanvasRenderingContext2D; + if (row.currentContext) { + context = row.currentContext; + } else { + context = row.collect ? SpAllAppStartupsChart.trace.canvasFavoritePanelCtx! : SpAllAppStartupsChart.trace.canvasPanelCtx!; + } + row.canvasSave(context); + (renders['all-app-start-up'] as AllAppStartupRender).renderMainThread( + { + appStartupContext: context, + useCache: useCache, + type: `app-startup ${row.rowId}`, + }, + row + ); + row.canvasRestore(context); + }; + SpAllAppStartupsChart.trace.rowsEL?.appendChild(row); + } +} \ No newline at end of file diff --git a/ide/src/trace/component/chart/SpChartManager.ts b/ide/src/trace/component/chart/SpChartManager.ts index 480f6d8867d8495390f78a50b57d7259fef86a12..da7713477aaff6a087a31559920f89cf18bf2a90 100644 --- a/ide/src/trace/component/chart/SpChartManager.ts +++ b/ide/src/trace/component/chart/SpChartManager.ts @@ -51,6 +51,7 @@ import { SpLogChart } from './SpLogChart'; import { SpHiSysEventChart } from './SpHiSysEventChart'; import { SpAllAppStartupsChart } from './SpAllAppStartups'; import { SpSegmentationChart } from './SpSegmentationChart'; +import { SpLtpoChart } from './SpLTPO'; export class SpChartManager { static APP_STARTUP_PID_ARR: Array = []; @@ -71,6 +72,7 @@ export class SpChartManager { private clockChart: SpClockChart; private irqChart: SpIrqChart; private spAllAppStartupsChart!: SpAllAppStartupsChart; + private SpLtpoChart!: SpLtpoChart; frameTimeChart: SpFrameTimeChart; public arkTsChart: SpArkTsChart; private logChart: SpLogChart; @@ -98,6 +100,7 @@ export class SpChartManager { this.logChart = new SpLogChart(trace); this.spHiSysEvent = new SpHiSysEventChart(trace); this.spAllAppStartupsChart = new SpAllAppStartupsChart(trace); + this.SpLtpoChart = new SpLtpoChart(trace) this.spSegmentationChart = new SpSegmentationChart(trace); } @@ -173,9 +176,10 @@ export class SpChartManager { progress('ark ts', 90); await this.arkTsChart.initFolder(); info('initData ark ts initialized'); + await this.spAllAppStartupsChart.init(); + await this.SpLtpoChart.init(); await this.frameTimeChart.init(); info('initData frameTimeLine initialized'); - await this.spAllAppStartupsChart.init(); progress('process', 92); await this.process.initAsyncFuncData(); await this.process.initDeliverInputEvent(); diff --git a/ide/src/trace/component/chart/SpLTPO.ts b/ide/src/trace/component/chart/SpLTPO.ts new file mode 100644 index 0000000000000000000000000000000000000000..dbe7c6bfa16a06a1e2d8050929affab808a53c52 --- /dev/null +++ b/ide/src/trace/component/chart/SpLTPO.ts @@ -0,0 +1,239 @@ +/* + * 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 { SpSystemTrace } from '../SpSystemTrace'; +import { TraceRow } from '../trace/base/TraceRow'; +import { renders } from '../../database/ui-worker/ProcedureWorker'; +import { CpuFreqStruct } from '../../database/ui-worker/ProcedureWorkerFreq'; +import { + queryPresentInfo, + queryFanceNameList, + queryFpsNameList +} from '../../database/SqlLite'; +import { LtpoRender, LtpoStruct } from '../../database/ui-worker/ProcedureWorkerLTPO' +import { HitchTimeStruct, hitchTimeRender } from '../../database/ui-worker/ProcedureWorkerHitchTime'; + +export class SpLtpoChart { + private readonly trace: SpSystemTrace | undefined; + static APP_STARTUP_PID_ARR: Array = []; + static jsonRow: TraceRow | undefined; + static trace: SpSystemTrace; + static presentArr: Array = []; + static fanceNameList: Array = []; + static fpsnameList: Array = []; + static ltpoDataArr: Array = []; + static sendLTPODataArr: Array = []; + static sendHitchDataArr: Array = []; + constructor(trace: SpSystemTrace) { + SpLtpoChart.trace = trace; + } + + async init() { + SpLtpoChart.ltpoDataArr = []; + SpLtpoChart.fanceNameList = await queryFanceNameList(); + SpLtpoChart.fpsnameList = await queryFpsNameList(); + SpLtpoChart.fanceNameList.map((item) => { + let cutFanceNameArr = item.name!.split(" "); + item.fanceId = Number(cutFanceNameArr[cutFanceNameArr.length - 1]); + }) + SpLtpoChart.fpsnameList.map((item) => { + let cutFpsNameArr = item.name!.split(",")[0].split(":"); + item.fps = Number(cutFpsNameArr[cutFpsNameArr.length - 1]); + }) + if (SpLtpoChart.fanceNameList!.length && SpLtpoChart.fpsnameList.length === SpLtpoChart.fanceNameList.length) { + for (let i = 0; i < SpLtpoChart.fanceNameList.length; i++) { + let tmpFps = SpLtpoChart.fpsnameList[i]!.fps ? Number(SpLtpoChart.fpsnameList[i]!.fps) : 60; + this.pushLtpoData( + SpLtpoChart.ltpoDataArr, + Number(SpLtpoChart.fanceNameList[i]!.fanceId!), + tmpFps, + 0, 0, 0, 0 + ); + } + } else { + return; + } + if (SpLtpoChart.fanceNameList && SpLtpoChart.fanceNameList.length) { + await this.initFolder(); + await this.initHitchTime(); + } + } + pushLtpoData( + lptoArr: any[] | undefined, + fanceId: Number, + fps: Number, + startTs: Number, + dur: Number, + nextStartTs: Number, + nextDur: number + ): void { + lptoArr?.push( + { + fanceId: fanceId, + fps: fps, + startTs: startTs, + dur: dur, + nextStartTs: nextStartTs, + nextDur: nextDur + } + ); + } + sendDataHandle(presentArr: LtpoStruct[], ltpoDataArr: LtpoStruct[]): Array { + let sendDataArr: LtpoStruct[] = []; + if (presentArr!.length && presentArr!.length === ltpoDataArr!.length) { + for (let i = 0; i < presentArr!.length; i++) { + ltpoDataArr[i].startTs = Number(presentArr[i].ts) - (window as any).recordStartNS; + ltpoDataArr[i].dur = presentArr[i].dur; + ltpoDataArr[i].nextStartTs = presentArr[i + 1] ? Number(presentArr[i + 1].ts) - (window as any).recordStartNS : ''; + ltpoDataArr[i].nextDur = presentArr[i + 1] ? presentArr[i + 1].dur : 0; + } + } else { + return sendDataArr; + } + for (let i = 0; i < ltpoDataArr.length; i++) { + if (ltpoDataArr[i].fanceId !== -1 && ltpoDataArr[i].nextDur) { + let sendStartTs: number | undefined = 0; + let sendDur: number | undefined = 0; + sendStartTs = Number(ltpoDataArr[i].startTs) + Number(ltpoDataArr[i].dur); + sendDur = Number(ltpoDataArr[i].nextStartTs) + Number(ltpoDataArr[i].nextDur) - sendStartTs; + let tmpDur = (Math.ceil(sendDur / 100000)) / 10; + if (tmpDur < 170) { + sendDataArr.push( + { + dur: sendDur, + value: 0, + startTs: sendStartTs, + pid: ltpoDataArr[i].fanceId, + itid: ltpoDataArr[i].fanceId, + name: undefined, + presentFance: ltpoDataArr[i].fanceId, + ts: undefined, + fanceId: ltpoDataArr[i].fanceId, + fps: ltpoDataArr[i].fps, + nextStartTs: ltpoDataArr[i].nextStartTs, + nextDur: ltpoDataArr[i].nextDur, + translateY: undefined, + frame: undefined, + isHover: false + } + ); + } + } + } + return sendDataArr; + } + async initFolder() { + SpLtpoChart.presentArr = []; + let row: TraceRow = TraceRow.skeleton(); + row.rowId = SpLtpoChart.fanceNameList!.length ? `LTPO ${SpLtpoChart.fanceNameList[0].fanceId}` : ''; + row.rowParentId = ''; + row.rowType = TraceRow.ROW_TYPE_LTPO; + row.folder = false; + row.style.height = '40px'; + row.name = `Lost Frames`; + row.favoriteChangeHandler = SpLtpoChart.trace.favoriteChangeHandler; + row.selectChangeHandler = SpLtpoChart.trace.selectChangeHandler; + row.supplier = async (): Promise> => { + SpLtpoChart.presentArr = await queryPresentInfo(); + SpLtpoChart.presentArr.map((item) => { + let cutPresentArr = item.name!.split(" "); + item.presentFance = Number(cutPresentArr[cutPresentArr.length - 1]); + }) + SpLtpoChart.sendLTPODataArr = this.sendDataHandle(SpLtpoChart.presentArr, SpLtpoChart.ltpoDataArr); + for (let i = 0; i < SpLtpoChart.sendLTPODataArr.length; i++) { + let tmpDur = SpLtpoChart.sendLTPODataArr[i].dur! / 1000000; + SpLtpoChart.sendLTPODataArr[i].value = (Math.round(tmpDur * Number(SpLtpoChart.sendLTPODataArr[i].fps) / 1000 - 1)) < 1 ? 0 : Math.round(tmpDur * Number(SpLtpoChart.sendLTPODataArr[i].fps) / 1000 - 1); + } + return SpLtpoChart.sendLTPODataArr; + } + row.focusHandler = (ev) => { + SpLtpoChart.trace?.displayTip( + row!, + LtpoStruct.hoverLtpoStruct, + `${(LtpoStruct.hoverLtpoStruct?.value!)}` + ) + }; + row.onThreadHandler = (useCache): void => { + let context: CanvasRenderingContext2D; + if (row.currentContext) { + context = row.currentContext; + } else { + context = row.collect ? SpLtpoChart.trace.canvasFavoritePanelCtx! : SpLtpoChart.trace.canvasPanelCtx!; + } + row.canvasSave(context); + (renders['ltpo-present'] as LtpoRender).renderMainThread( + { + appStartupContext: context, + useCache: useCache, + type: `ltpo-present ${row.rowId}`, + }, + row + ); + row.canvasRestore(context); + }; + SpLtpoChart.trace.rowsEL?.appendChild(row); + } + async initHitchTime() { + SpLtpoChart.presentArr = []; + let row: TraceRow = TraceRow.skeleton(); + row.rowId = SpLtpoChart.fanceNameList!.length ? `hitch-time ${SpLtpoChart.fanceNameList[0].fanceId}` : ''; + row.rowParentId = ''; + row.rowType = TraceRow.ROW_TYPE_HITCH_TIME; + row.folder = false; + row.style.height = '40px'; + row.name = `Hitch Time`; + row.favoriteChangeHandler = SpLtpoChart.trace.favoriteChangeHandler; + row.selectChangeHandler = SpLtpoChart.trace.selectChangeHandler; + row.supplier = async (): Promise> => { + SpLtpoChart.presentArr = await queryPresentInfo(); + SpLtpoChart.presentArr.map((item) => { + let cutPresentArr = item.name!.split(" "); + item.presentFance = Number(cutPresentArr[cutPresentArr.length - 1]); + }) + SpLtpoChart.sendHitchDataArr = this.sendDataHandle(SpLtpoChart.presentArr, SpLtpoChart.ltpoDataArr); + for (let i = 0; i < SpLtpoChart.sendHitchDataArr.length; i++) { + let tmpVale = Number((Math.ceil(((SpLtpoChart.sendHitchDataArr[i].dur! / 1000000) - (1000 / SpLtpoChart.sendHitchDataArr[i].fps!)) * 10)) / 10); + SpLtpoChart.sendHitchDataArr[i].value = tmpVale! < 0 ? 0 : tmpVale; + } + return SpLtpoChart.sendHitchDataArr; + } + row.focusHandler = (ev) => { + SpLtpoChart.trace?.displayTip( + row!, + HitchTimeStruct.hoverHitchTimeStruct, + `${(HitchTimeStruct.hoverHitchTimeStruct?.value!)}` + ) + }; + row.onThreadHandler = (useCache): void => { + let context: CanvasRenderingContext2D; + if (row.currentContext) { + context = row.currentContext; + } else { + context = row.collect ? SpLtpoChart.trace.canvasFavoritePanelCtx! : SpLtpoChart.trace.canvasPanelCtx!; + } + row.canvasSave(context); + (renders['hitch'] as hitchTimeRender).renderMainThread( + { + appStartupContext: context, + useCache: useCache, + type: `hitch ${row.rowId}`, + }, + row + ); + row.canvasRestore(context); + }; + SpLtpoChart.trace.rowsEL?.appendChild(row); + } +} diff --git a/ide/src/trace/component/trace/base/RangeSelect.ts b/ide/src/trace/component/trace/base/RangeSelect.ts index a052cd6c645fe05af38bad3917047056857eb631..306068f105b556131c3859a494517473d9704072 100644 --- a/ide/src/trace/component/trace/base/RangeSelect.ts +++ b/ide/src/trace/component/trace/base/RangeSelect.ts @@ -19,7 +19,8 @@ import { ns2x, TimerShaftElement } from '../TimerShaftElement'; import { info } from '../../../../log/Log'; import './Extension'; import { SpSystemTrace } from '../../SpSystemTrace'; -import { querySearchRowFuncData } from '../../../database/SqlLite'; +import { fuzzyQueryFuncRowData, queryFuncRowData } from '../../../database/SqlLite'; +import { SpLtpoChart } from '../../chart/SpLTPO'; export class RangeSelect { private rowsEL: HTMLDivElement | undefined | null; @@ -80,35 +81,106 @@ export class RangeSelect { if (this.selectHandler) { this.selectHandler(this.rangeTraceRow || [], !this.isHover); } - //如果只框选了一条泳道,查询H:RSMainThread::DoComposition数据 - let docompositionData: Array = []; - if (this.rangeTraceRow) { - this.rangeTraceRow.forEach((row) => { - row.docompositionList = []; - }); - docompositionData = []; + //查询H:RSMainThread::DoComposition数据 + if (this.rangeTraceRow?.length) { + let judgeRowName: boolean = this.checkProcessName(this.rangeTraceRow, 'render_service'); if ( - this.rangeTraceRow.length === 1 && - this.rangeTraceRow[0]?.getAttribute('row-type') === 'func' && - this.rangeTraceRow[0]?.getAttribute('name')?.startsWith('render_service') + // 如果框选的第一行和最后一行的父节点名称都以render_service开头 + this.rangeTraceRow[0]!.parentRowEl?.getAttribute('name')?.startsWith('render_service') && + this.rangeTraceRow[this.rangeTraceRow.length - 1].parentRowEl?.getAttribute('name')?.startsWith('render_service') ) { - querySearchRowFuncData( - 'H:RSMainThread::DoComposition', - Number(this.rangeTraceRow[0]?.getAttribute('row-id')), - TraceRow.rangeSelectObject!.startNS!, - TraceRow.rangeSelectObject!.endNS! - ).then((res) => { - res.forEach((item) => { - docompositionData.push(item.startTime!); - }); - this.rangeTraceRow![0].docompositionList = docompositionData; - }); + this.handleFrameRateData(this.rangeTraceRow!, 'render_service', 'H:RSMainThread::DoComposition'); + this.handleFrameRateData(this.rangeTraceRow!, 'RSHardwareThrea', 'H:Repaint'); + this.handleFrameRateData(this.rangeTraceRow!, 'present', 'H:Waiting for present Fence'); + } else if (judgeRowName) { + this.handleFrameRateData(this.rangeTraceRow[0].childrenList, 'render_service', 'H:RSMainThread::DoComposition'); + this.handleFrameRateData(this.rangeTraceRow[0].childrenList, 'RSHardwareThrea', 'H:Repaint'); + this.handleFrameRateData(this.rangeTraceRow[0].childrenList, 'present', 'H:Waiting for present Fence'); } } } this.isMouseDown = false; } +// 检查框选进程是否为render-service +checkProcessName(selectRangeRow: Array>, processName: string) { + let judgeName: boolean = false; + for (const item of selectRangeRow) { + if ( + item.childrenList.length && + item.getAttribute('name')?.startsWith(processName) + ) { + judgeName = true; + break; + } + } + return judgeName +} + +// 根据框选行和方法名查询数据 +handleFrameRateData(rowList: Array>, rowName: string, funcName: string): void { + let dataList: Array = []; + for (let i = 0; i < rowList.length; i++) { + if (rowList[i].getAttribute('row-type') === 'func') { + // console.log('funcName', funcName); + if (rowList[i]?.getAttribute('name')?.startsWith(rowName)) { + queryFuncRowData( + funcName, + Number(rowList[i]?.getAttribute('row-id')), + TraceRow.rangeSelectObject!.startNS!, + TraceRow.rangeSelectObject!.endNS!, + ).then((res) => { + if (res.length >= 2) { + res.forEach((item) => { + dataList.push(item.startTime!) + }); + rowList[i].frameRateList = dataList; + } + }); + } + if (rowName === 'present' && rowList[i]?.getAttribute('name')?.startsWith(rowName)) { + this.handlePresentData(rowList[i], funcName) + } + if (dataList.length) { + break; + } + } + } +} + +// 处理框选时present线程的数据 +handlePresentData(currentRow: TraceRow, funcName: string): void { + let dataList: Array = []; + fuzzyQueryFuncRowData( + funcName, + Number(currentRow?.getAttribute('row-id')), + TraceRow.rangeSelectObject!.startNS!, + TraceRow.rangeSelectObject!.endNS!, + ).then((res) => { + if (res.length >= 2) { + res.forEach((item) => { + dataList.push(item.endTime!) + }); + currentRow.frameRateList = dataList; + if (currentRow.frameRateList.length >= 2) { + let hitchTimeList: Array = [] + for (let i = 0; i < SpLtpoChart.sendHitchDataArr.length; i++) { + if (SpLtpoChart.sendHitchDataArr[i].startTs! >= dataList[0] + && + SpLtpoChart.sendHitchDataArr[i].startTs! < dataList[dataList.length - 1]) { + hitchTimeList.push(SpLtpoChart.sendHitchDataArr[i].value!) + } + if (SpLtpoChart.sendHitchDataArr[i].startTs! >= dataList[dataList.length - 1]) { + break; + } + } + let sum: number = hitchTimeList.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + let hitchRate: number = (sum / ((TraceRow.rangeSelectObject!.endNS! - TraceRow.rangeSelectObject!.startNS!) / 1000000)) + currentRow.hitchRateData = hitchRate + } + } + }); +} isDrag(): boolean { return this.startPageX != this.endPageX; } @@ -256,9 +328,17 @@ export class RangeSelect { } }); if (this.rangeTraceRow && this.rangeTraceRow.length) { - this.rangeTraceRow!.forEach((row) => { - row.docompositionList = []; - }); + if (this.rangeTraceRow[0].parentRowEl) { + for (let i = 0; i < this.rangeTraceRow[0].parentRowEl.childrenList.length; i++) { + this.rangeTraceRow[0].parentRowEl.childrenList[i].frameRateList = []; + this.rangeTraceRow[0].parentRowEl.childrenList[i].hitchRateData = null; + } + } else { + for (let j = 0; j < this.rangeTraceRow[0].childrenList.length; j++) { + this.rangeTraceRow[0].childrenList[j].frameRateList = []; + this.rangeTraceRow[0].childrenList[j].hitchRateData = null; + } + } } this.timerShaftEL!.sportRuler!.isRangeSelect = this.rangeTraceRow?.length > 0; this.timerShaftEL!.sportRuler!.draw(); diff --git a/ide/src/trace/component/trace/base/TraceRow.ts b/ide/src/trace/component/trace/base/TraceRow.ts index c1ba67b7cc06e945f1024e407f0f393043dbb9a1..b1bb2f61fce3328cf4d5870be8868d9c612f4ba0 100644 --- a/ide/src/trace/component/trace/base/TraceRow.ts +++ b/ide/src/trace/component/trace/base/TraceRow.ts @@ -182,12 +182,15 @@ export class TraceRow extends HTMLElement { childrenList: Array> = []; parentRowEl: TraceRow | undefined; _rowSettingList: Array | null | undefined; - _docompositionList: Array | undefined; + _frameRateList: Array | undefined; + _hitchRateData: number | undefined | null focusHandler?: (ev: MouseEvent) => void | undefined; findHoverStruct?: () => void | undefined; public funcMaxHeight: number = 0; currentContext: CanvasRenderingContext2D | undefined | null; + static ROW_TYPE_HITCH_TIME: string | null | undefined; + static ROW_TYPE_LTPO: string | null | undefined; constructor( args: { @@ -197,12 +200,12 @@ export class TraceRow extends HTMLElement { isOffScreen: boolean; skeleton?: boolean; } = { - canvasNumber: 1, - alpha: false, - contextId: '2d', - isOffScreen: true, - skeleton: false, - } + canvasNumber: 1, + alpha: false, + contextId: '2d', + isOffScreen: true, + skeleton: false, + } ) { super(); this.args = args; @@ -245,12 +248,21 @@ export class TraceRow extends HTMLElement { 'row-setting-popover-direction', ]; } - get docompositionList(): Array | undefined { - return this._docompositionList; + + get frameRateList(): Array | undefined { + return this._frameRateList; + } + + set frameRateList(value: Array | undefined) { + this._frameRateList = value; + } + + get hitchRateData(): number | undefined | null { + return this._hitchRateData; } - set docompositionList(value: Array | undefined) { - this._docompositionList = value; + set hitchRateData(value: number | undefined | null) { + this._hitchRateData = value; } get funcExpand(): boolean { diff --git a/ide/src/trace/component/trace/sheet/process/TabPaneThreadUsage.ts b/ide/src/trace/component/trace/sheet/process/TabPaneThreadUsage.ts index 4a8db661cec919264f2484a58c140f4494aa777a..7882c2335bff8bc15599d617ead4889cad4a0709 100644 --- a/ide/src/trace/component/trace/sheet/process/TabPaneThreadUsage.ts +++ b/ide/src/trace/component/trace/sheet/process/TabPaneThreadUsage.ts @@ -75,6 +75,10 @@ export class TabPaneThreadUsage extends BaseElement { if (result != null && result.length > 0) { log('getTabThreadStates result size : ' + result.length); let filterArr = result.filter((it) => threadUsageParam.processIds.includes(it.pid)); + let totalDurtion = 0; + filterArr.forEach((item) => { + totalDurtion = totalDurtion + item.wallDuration; + }) let map: Map = new Map(); for (let resultEl of filterArr) { if (threadUsageParam.processIds.includes(resultEl.pid)) { @@ -83,7 +87,7 @@ export class TabPaneThreadUsage extends BaseElement { map.get(resultEl.tid)[`cpu${resultEl.cpu}TimeStr`] = getProbablyTime(resultEl.wallDuration || 0); map.get(resultEl.tid)[`cpu${resultEl.cpu}Ratio`] = ( (100.0 * (resultEl.wallDuration || 0)) / - (threadUsageParam.rightNs - threadUsageParam.leftNs) + (totalDurtion) ).toFixed(2); map.get(resultEl.tid)[`wallDuration`] = map.get(resultEl.tid)[`wallDuration`] + (resultEl.wallDuration || 0); @@ -108,7 +112,7 @@ export class TabPaneThreadUsage extends BaseElement { threadStatesStruct[`cpu${resultEl.cpu}TimeStr`] = getProbablyTime(resultEl.wallDuration || 0); threadStatesStruct[`cpu${resultEl.cpu}Ratio`] = ( (100.0 * (resultEl.wallDuration || 0)) / - (threadUsageParam.rightNs - threadUsageParam.leftNs) + (totalDurtion) ).toFixed(2); map.set(resultEl.tid, threadStatesStruct); } diff --git a/ide/src/trace/database/SqlLite.ts b/ide/src/trace/database/SqlLite.ts index a3e9e84e209fffe5ffc9526658fe0e8689e664d2..9a4e7268fb18d17292f9abc523cf527d289eb1fc 100644 --- a/ide/src/trace/database/SqlLite.ts +++ b/ide/src/trace/database/SqlLite.ts @@ -85,6 +85,7 @@ import { HiSysEventStruct } from './ui-worker/ProcedureWorkerHiSysEvent'; import { KeyPathStruct } from '../bean/KeyPathStruct'; import { FuncNameCycle, BinderItem } from '../bean/BinderProcessThread'; import { GpuCountBean, SearchGpuFuncBean } from '../bean/GpufreqBean'; +import { LtpoStruct } from './ui-worker/ProcedureWorkerLTPO'; class DataWorkerThread { taskMap: any = {}; @@ -1545,6 +1546,94 @@ export const querySingleAppStartupsName = (pid: number): Promise> => where pid=$pid`, { $pid: pid } ); + export const queryPresentInfo =(): Promise > => + query( + 'queryPresentInfo', + `SELECT ts,dur,name FROM "callstack" WHERE callid in (SELECT id FROM "thread" WHERE name LIKE('Present%')) + AND name LIKE('H:Waiting for Present Fence%')` + ) + + export const queryFanceNameList = ():Promise> => + query( + 'queryFanceNameList', + `SELECT ts,dur,name FROM "callstack" WHERE callid in (SELECT id FROM "thread" WHERE name LIKE('RSHardwareThrea%')) + AND name LIKE('H:Present Fence%')` + ) + + export const queryFpsNameList = ():Promise> => + query( + 'queryFpsNameList', + `SELECT name FROM "callstack" WHERE callid in (SELECT id FROM "thread" WHERE name LIKE('RSHardwareThrea%')) + AND name LIKE('%Layers rate%')` + ) + export const queryFuncRowData = ( + funcName: string, + tIds: number, + leftNS: number, + rightNS: number + ): Promise> => + query( + 'queryFuncRowData', + ` + select + c.name as funName, + c.ts - r.start_ts as startTime + 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 like '${funcName}' + and + t.tid = ${tIds} + and + not ((startTime < ${leftNS}) or (startTime > ${rightNS})); + `, + { $search: funcName } + ); + + export const fuzzyQueryFuncRowData = ( + funcName: string, + tIds: number, + leftNS: number, + rightNS: number + ): Promise> => + query( + 'fuzzyQueryFuncRowData', + ` + select + c.name as funName, + c.ts - r.start_ts as startTime, + c.ts - r.start_ts + c.dur as endTime + 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 like '%${funcName}%' + and + t.tid = ${tIds} + and + not ((endTime < ${leftNS}) or (endTime > ${rightNS})); + `, + { $search: funcName } + ); export const queryProcessSoMaxDepth = (): Promise> => query( diff --git a/ide/src/trace/database/ui-worker/ProcedureWorker.ts b/ide/src/trace/database/ui-worker/ProcedureWorker.ts index 5382eb22358e87ae7ef7b03fc64c0c100f17c3ce..5218c4276c91416c6d278e15dfc9deee04e19de0 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorker.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorker.ts @@ -61,6 +61,8 @@ import { HiSysEventRender } from './ProcedureWorkerHiSysEvent'; import { AllAppStartupRender } from './ProcedureWorkerAllAppStartup'; import { FreqExtendRender } from './ProcedureWorkerFreqExtend'; import { BinderRender } from './procedureWorkerBinder'; +import { hitchTimeRender } from './ProcedureWorkerHitchTime'; +import { LtpoRender } from './ProcedureWorkerLTPO'; let dataList: any = {}; let dataList2: any = {}; @@ -81,6 +83,8 @@ export let renders: any = { process: new ProcessRender(), 'app-start-up': new AppStartupRender(), 'all-app-start-up': new AllAppStartupRender(), + 'ltpo-present': new LtpoRender(), + 'hitch': new hitchTimeRender(), 'app-so-init': new SoRender(), heap: new HeapRender(), 'heap-timeline': new HeapTimelineRender(), diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerCommon.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerCommon.ts index 7b804d5bcc3297c54f19597819f1e22ae28fd3d5..cf8a86783ce7800e4d8867aef531088606a1ecee 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorkerCommon.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerCommon.ts @@ -48,10 +48,10 @@ export class RequestMessage { totalNS: any; slicesTime: | { - startTime: number | null; - endTime: number | null; - color: string | null; - } + startTime: number | null; + endTime: number | null; + color: string | null; + } | undefined; range: any; scale: any; @@ -64,9 +64,9 @@ export class RequestMessage { id: any; postMessage: | { - (message: any, targetOrigin: string, transfer?: Transferable[]): void; - (message: any, options?: WindowPostMessageOptions): void; - } + (message: any, targetOrigin: string, transfer?: Transferable[]): void; + (message: any, options?: WindowPostMessageOptions): void; + } | undefined; } @@ -99,8 +99,8 @@ export function ns2Timestamp(ns: number): string { return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second .toString() .padStart(2, '0')}:${millisecond.toString().padStart(3, '0')}:${microsecond - .toString() - .padStart(3, '0')}:${nanosecond.toString().padStart(3, '0')}`; + .toString() + .padStart(3, '0')}:${nanosecond.toString().padStart(3, '0')}`; } const offsetX = 5; @@ -539,10 +539,10 @@ export function drawFlagLine( frame: any, slicesTime: | { - startTime: number | null | undefined; - endTime: number | null | undefined; - color: string | null | undefined; - } + startTime: number | null | undefined; + endTime: number | null | undefined; + color: string | null | undefined; + } | undefined ) { if (commonCtx) { @@ -746,104 +746,122 @@ export function drawSelectionRange(context: any, params: TraceRow) { } // 绘制方法H:RSMainThread::DoComposition平均帧率的箭头指示线条 - if (params._docompositionList?.length) { - const rateList: Array = [...new Set(params.docompositionList)]; - if (rateList.length >= 2) { - // 计算平均帧率 - let cutres: number = rateList[rateList.length - 1]! - rateList[0]!; - let avgFrameRate: string = (((rateList.length - 1) / cutres) * 1000000000).toFixed(1) + 'fps'; - - let avgRateStartX = Math.floor( - ns2x( - rateList[0]!, - TraceRow.range?.startNS ?? 0, - TraceRow.range?.endNS ?? 0, - TraceRow.range?.totalNS ?? 0, - params.frame - ) - ); - let avgRateEndX = Math.floor( - ns2x( - rateList[rateList.length - 1]!, - TraceRow.range?.startNS ?? 0, - TraceRow.range?.endNS ?? 0, - TraceRow.range?.totalNS ?? 0, - params.frame - ) - ); - const textWidth = context.measureText(avgFrameRate).width; - const textHeight = 25; - const padding = 5; - let textX = - Math.floor( - ns2x( - (rateList[0]! + rateList[rateList.length - 1]!) / 2, - TraceRow.range?.startNS ?? 0, - TraceRow.range?.endNS ?? 0, - TraceRow.range?.totalNS ?? 0, - params.frame - ) - ) - - textWidth / 2; - const textY = params.frame.y + 25; - - //左移到边界,不画线和文字 - if (avgRateStartX <= 0) { - avgRateStartX = -100; - } - if (avgRateEndX <= 0) { - avgRateEndX = -100; - } - if (textX <= 0) { - textX = -100; - } - //右移到边界,不画线和文字 - if (textX + textWidth / 2 >= params.frame.width) { - textX = params.frame.width + 100; - } - if (avgRateStartX >= params.frame.width) { - avgRateStartX = params.frame.width + 100; - } - if (avgRateEndX >= params.frame.width) { - avgRateEndX = params.frame.width + 100; - } - // 绘制文字背景矩形 - context.fillStyle = 'red'; - context.fillRect( - textX - padding, - textY - textHeight + padding, - textWidth + padding * 2, - textHeight - padding * 2 - ); + if (params.frameRateList && params.frameRateList.length) { + changeFrameRatePoint(params.frameRateList, context, params); + }; + } +} - context.lineWidth = 2; - context.strokeStyle = 'yellow'; - context.beginPath(); - context.moveTo(avgRateStartX, textY); - context.lineTo(avgRateEndX, textY); - context.stroke(); - - const arrowSize = 5.5; - const arrowHead = (x: number, y: number, direction: 'left' | 'right') => { - context.beginPath(); - const headX = x + (direction === 'left' ? arrowSize : -arrowSize); - const headY = y - arrowSize / 2; - context.moveTo(x, y); - context.lineTo(headX, headY); - context.lineTo(headX, y + arrowSize); - context.closePath(); - - context.fillStyle = 'yellow'; - context.fill(); - }; - arrowHead(avgRateStartX, textY - 1, 'left'); - arrowHead(avgRateEndX, textY - 1, 'right'); - - context.fillStyle = 'white'; - context.fillText(avgFrameRate, textX, textY - 8); - } - } +// 转换起始点坐标 +function changeFrameRatePoint(arrList: Array, ctx: any, selectParams: TraceRow): void { + const rateList: Array = [...new Set(arrList)]; + let avgRateStartX = Math.floor( + ns2x( + rateList[0]!, + TraceRow.range?.startNS ?? 0, + TraceRow.range?.endNS ?? 0, + TraceRow.range?.totalNS ?? 0, + selectParams.frame + ) + );// 起始坐标 + + let avgRateEndX = Math.floor( + ns2x( + rateList[rateList.length - 1]!, + TraceRow.range?.startNS ?? 0, + TraceRow.range?.endNS ?? 0, + TraceRow.range?.totalNS ?? 0, + selectParams.frame + ) + );// 结束坐标 + + drawavgFrameRate(rateList, ctx, selectParams, avgRateStartX, avgRateEndX); // 绘制 +} + +// 计算平均帧率 +function calculateAvgRate(arr: Array) { + let cutres: number = (arr[arr.length - 1]! - arr[0]!); // 结束时间-开始时间 + let avgRate: string = ((arr.length - 1) / cutres * 1000000000).toFixed(1); // 帧数/时间差 * 1000000000 四舍五入保留一位小数 + return avgRate; +} + +// 绘制平均帧率箭头指示线条 +function drawavgFrameRate(arrList: Array, ctx: any, selectParams: TraceRow, startX: number, endX: number): void { + // 获取平均帧率 + let avgFrameRate: string = calculateAvgRate(arrList) + 'fps'; + if (selectParams.hitchRateData !== null && selectParams.hitchRateData !== undefined) { + let hitchRate: string = (Number(selectParams.hitchRateData) * 100).toFixed(2) + '%'; + avgFrameRate = 'RS:' + ' ' + calculateAvgRate(arrList) + 'fps' + ' ' + ',' + ' ' + 'HitchRate:' + ' ' + hitchRate; + } else { + avgFrameRate = 'RS:' + ' ' + calculateAvgRate(arrList) + 'fps'; + } + + const textWidth = ctx.measureText(avgFrameRate).width; // 测量文本的宽度 + const textHeight = 25; //文本的高度 + const padding = 5; //内边距 + let textX = Math.floor(ns2x( + (arrList[0]! + arrList[arrList.length - 1]!) / 2, + TraceRow.range?.startNS ?? 0, + TraceRow.range?.endNS ?? 0, + TraceRow.range?.totalNS ?? 0, + selectParams.frame + )) - textWidth / 2; //根据帧率范围的中间值计算文本的起始x坐标 + const textY = selectParams.frame.y + 10; + + //左移到边界,不画线和文字 + if (startX <= 0) { + startX = -100; + } + if (endX <= 0) { + endX = -100; + } + if (textX <= 0) { + textX = -100; + } + //右移到边界,不画线和文字 + if (textX + textWidth / 2 >= selectParams.frame.width) { + textX = selectParams.frame.width + 100; } + if (startX >= selectParams.frame.width) { + startX = selectParams.frame.width + 100; + } + if (endX >= selectParams.frame.width) { + endX = selectParams.frame.width + 100; + } + + // 设置横线的宽度、颜色并绘制 + ctx.lineWidth = 2; + ctx.strokeStyle = 'yellow'; + + ctx.beginPath(); + ctx.moveTo(startX, textY); + ctx.lineTo(endX, textY); + ctx.stroke(); + + // 定义箭头的大小和绘制箭头的函数 + const arrowSize = 5.5; + const arrowHead = (x: number, y: number, direction: 'left' | 'right') => { + ctx.beginPath(); + const headX = x + (direction === 'left' ? arrowSize : -arrowSize); + const headY = y - arrowSize / 2; + ctx.moveTo(x, y); + ctx.lineTo(headX, headY); + ctx.lineTo(headX, y + arrowSize); + ctx.closePath(); + + ctx.fillStyle = 'yellow'; + ctx.fill(); + }; + arrowHead(startX, textY - 1, 'left'); + arrowHead(endX, textY - 1, 'right'); + + // 绘制文字背景矩形 + ctx.fillStyle = 'red'; + ctx.fillRect(textX - padding, textY - textHeight / 2 + padding, textWidth + padding * 2, textHeight - padding * 2); + + // 绘制文字 + ctx.fillStyle = 'white'; + ctx.fillText(avgFrameRate, textX, textY + 4); } export function drawWakeUp( @@ -1210,7 +1228,7 @@ export function drawLoading( frame: any, left: number, right: number -) {} +) { } export function drawString(ctx: CanvasRenderingContext2D, str: string, textPadding: number, frame: Rect, data: any) { if (data.textMetricsWidth === undefined) { diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerHitchTime.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerHitchTime.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f5195c2e057977840e1844fca893497d0929a6e --- /dev/null +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHitchTime.ts @@ -0,0 +1,112 @@ +/* + * 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 { BaseStruct, dataFilterHandler } from './ProcedureWorkerCommon'; +import { TraceRow } from '../../component/trace/base/TraceRow'; + +export class hitchTimeRender { + renderMainThread( + req: { + appStartupContext: CanvasRenderingContext2D; + useCache: boolean; + type: string; + }, + ltpoRow: TraceRow + ): void { + let list = ltpoRow.dataList; + HitchTimeStruct.maxVal = 0; + for (let i = 0; i < list.length; i++) { + if (Number(list[i].value) > HitchTimeStruct.maxVal) { + HitchTimeStruct.maxVal = Number(list[i].value) + }; + } + let filter = ltpoRow.dataListCache; + dataFilterHandler(list, filter, { + startKey: 'startTs', + durKey: 'dur', + startNS: TraceRow.range?.startNS ?? 0, + endNS: TraceRow.range?.endNS ?? 0, + totalNS: TraceRow.range?.totalNS ?? 0, + frame: ltpoRow.frame, + paddingTop: 5, + useCache: req.useCache || !(TraceRow.range?.refresh ?? false), + }); + req.appStartupContext.globalAlpha = 0.6; + let find = false; + let offset = 3; + for (let re of filter) { + if (ltpoRow.isHover) { + if ( + re.frame && + ltpoRow.hoverX >= re.frame.x - offset && + ltpoRow.hoverX <= re.frame.x + re.frame.width + offset + ) { + HitchTimeStruct.hoverHitchTimeStruct = re; + find = true; + } + } + if (!ltpoRow.isHover) HitchTimeStruct.hoverHitchTimeStruct = undefined + if (!find && ltpoRow.isHover) { + HitchTimeStruct.hoverHitchTimeStruct = undefined; + } + req.appStartupContext.beginPath() + HitchTimeStruct.draw(req.appStartupContext, re); + req.appStartupContext.closePath() + } + } +} + + +export class HitchTimeStruct extends BaseStruct { + static hoverHitchTimeStruct: HitchTimeStruct | undefined; + static selectHitchTimeStruct: HitchTimeStruct | undefined; + static maxVal: number = 0; + dur: number | undefined; + name: string | undefined; + presentFance: number | undefined; + ts: number | undefined; + fanceId: number | undefined; + fps: number | undefined; + startTs: number | undefined; + nextStartTs: string | number | undefined; + nextDur: number | undefined; + value: number | undefined; + pid: number | undefined; + itid: number | undefined; + + static draw(ctx: CanvasRenderingContext2D, data: HitchTimeStruct): void { + if (data.frame) { + ctx.fillStyle = '#9933FA'; + if (data === HitchTimeStruct.hoverHitchTimeStruct || data === HitchTimeStruct.selectHitchTimeStruct) { + let drawHeight: number = Math.floor( + ((Number(data.value) || 0) * (data.frame.height || 0) * 1.0) / HitchTimeStruct.maxVal! + ); + drawHeight = drawHeight < 1 ? 1 : drawHeight + ctx.globalAlpha = 1.0; + ctx.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, data.frame.width, drawHeight); + ctx.lineWidth = 1; + ctx.strokeStyle = ' #0000FF'; + ctx.strokeRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, data.frame.width, drawHeight) + } else { + ctx.globalAlpha = 0.6; + let drawHeight: number = Math.floor(((Number(data.value) || 0) * (data.frame.height || 0)) / HitchTimeStruct.maxVal!); + drawHeight = drawHeight < 1 ? 1 : drawHeight + ctx.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, data.frame.width, drawHeight) + } + + } + } + +} diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerLTPO.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerLTPO.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb986a5cf391107a984e1a2aa31a56949d2dca89 --- /dev/null +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerLTPO.ts @@ -0,0 +1,110 @@ +/* + * 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 { BaseStruct, dataFilterHandler } from './ProcedureWorkerCommon'; +import { TraceRow } from '../../component/trace/base/TraceRow'; + +export class LtpoRender { + renderMainThread( + req: { + appStartupContext: CanvasRenderingContext2D; + useCache: boolean; + type: string; + }, + ltpoRow: TraceRow + ): void { + let list = ltpoRow.dataList; + LtpoStruct.maxVal = 0; + for (let i = 0; i < list.length; i++) { + if (Number(list[i].value) > LtpoStruct.maxVal) LtpoStruct.maxVal = Number(list[i].value); + } + let filter = ltpoRow.dataListCache; + dataFilterHandler(list, filter, { + startKey: 'startTs', + durKey: 'dur', + startNS: TraceRow.range?.startNS ?? 0, + endNS: TraceRow.range?.endNS ?? 0, + totalNS: TraceRow.range?.totalNS ?? 0, + frame: ltpoRow.frame, + paddingTop: 5, + useCache: req.useCache || !(TraceRow.range?.refresh ?? false), + }); + req.appStartupContext.globalAlpha = 0.6; + let find = false; + let offset = 3; + for (let re of filter) { + if (ltpoRow.isHover) { + if ( + re.frame && + ltpoRow.hoverX >= re.frame.x - offset && + ltpoRow.hoverX <= re.frame.x + re.frame.width + offset + ) { + LtpoStruct.hoverLtpoStruct = re; + find = true; + } + } + if(!ltpoRow.isHover) LtpoStruct.hoverLtpoStruct = undefined + if (!find && ltpoRow.isHover) { + LtpoStruct.hoverLtpoStruct = undefined; + } + req.appStartupContext.beginPath() + LtpoStruct.draw(req.appStartupContext, re); + req.appStartupContext.closePath() + } + } +} + + +export class LtpoStruct extends BaseStruct { + static hoverLtpoStruct: LtpoStruct | undefined; + static selectLtpoStruct: LtpoStruct | undefined; + static maxVal: number | undefined; + dur: number | undefined; + name: string | undefined; + presentFance: number | undefined; + ts: number | undefined; + fanceId: number | undefined; + fps: number | undefined; + startTs: number | undefined; + nextStartTs: string | number | undefined; + nextDur: number | undefined; + value: number | undefined ; + pid: number | undefined; + itid: number | undefined; + + static draw(ctx: CanvasRenderingContext2D, data: LtpoStruct): void { + if (data.frame) { + ctx.fillStyle = '#9933FA'; + if (data === LtpoStruct.hoverLtpoStruct || data === LtpoStruct.selectLtpoStruct) { + let drawHeight: number = Math.floor( + ((Number(data.value) || 0) * (data.frame.height || 0) * 1.0) / LtpoStruct.maxVal! + ); + drawHeight = drawHeight < 1 ? 1 : drawHeight + ctx.globalAlpha = 1.0; + ctx.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, data.frame.width, drawHeight); + ctx.lineWidth = 1; + ctx.strokeStyle = ' #0000FF'; + ctx.strokeRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, data.frame.width, drawHeight) + } else { + ctx.globalAlpha = 0.6; + let drawHeight: number = Math.floor(((Number(data.value) || 0) * (data.frame.height || 0)) / LtpoStruct.maxVal!); + drawHeight = drawHeight < 1 ? 1 : drawHeight + ctx.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, data.frame.width, drawHeight) + } + + } + } + +}