diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index f909b021eb39021c63018b15cc7064a1d9ae99c7..877d9eb5051aa63db965c6c8dfef4bfc608b00f0 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -742,8 +742,8 @@ export class SpSystemTrace extends BaseElement { let isIntersect = (filterFunc: FuncStruct, rangeData: RangeSelectStruct) => Math.max(filterFunc.startTs! + filterFunc.dur!, rangeData!.endNS || 0) - - Math.min(filterFunc.startTs!, rangeData!.startNS || 0) < - filterFunc.dur! + (rangeData!.endNS || 0) - (rangeData!.startNS || 0) && + Math.min(filterFunc.startTs!, rangeData!.startNS || 0) < + filterFunc.dur! + (rangeData!.endNS || 0) - (rangeData!.startNS || 0) && filterFunc.funName!.indexOf('H:Task ') >= 0; let taskData = it.dataList.filter((taskData: FuncStruct) => { taskData!.tid = parseInt(it.rowId!); @@ -1128,7 +1128,7 @@ export class SpSystemTrace extends BaseElement { } else if (it.rowType == TraceRow.ROW_TYPE_JANK) { let isIntersect = (filterJank: JanksStruct, rangeData: RangeSelectStruct) => Math.max(filterJank.ts! + filterJank.dur!, rangeData!.endNS || 0) - - Math.min(filterJank.ts!, rangeData!.startNS || 0) < + Math.min(filterJank.ts!, rangeData!.startNS || 0) < filterJank.dur! + (rangeData!.endNS || 0) - (rangeData!.startNS || 0); if (it.name == 'Actual Timeline') { selection.jankFramesData = []; @@ -1211,7 +1211,7 @@ export class SpSystemTrace extends BaseElement { } else if (it.rowType == TraceRow.ROW_TYPE_FRAME_ANIMATION) { let isIntersect = (animationStruct: FrameAnimationStruct, selectStruct: RangeSelectStruct) => Math.max(animationStruct.startTs! + animationStruct.dur!, selectStruct!.endNS || 0) - - Math.min(animationStruct.startTs!, selectStruct!.startNS || 0) < + Math.min(animationStruct.startTs!, selectStruct!.startNS || 0) < animationStruct.dur! + (selectStruct!.endNS || 0) - (selectStruct!.startNS || 0); let frameAnimationList = it.dataList.filter((frameAnimationBean: FrameAnimationStruct) => { return isIntersect(frameAnimationBean, TraceRow.rangeSelectObject!); @@ -1308,6 +1308,15 @@ export class SpSystemTrace extends BaseElement { } this.timerShaftEL!.selectionList.push(selection); // 保持选中对象,为后面的再次选中该框选区域做准备。 this.selectionParam = selection; + + let currentRangeTid: Array = this.selectionParam.funTids + // 如果只框选了一个方法行 + if (currentRangeTid.length === 1) { + TraceRow.currentRowId = currentRangeTid[0] + setTimeout(() => { + this.refreshCanvas(true) + }, 50); + } }; // @ts-ignore new ResizeObserver((entries) => { @@ -1391,7 +1400,7 @@ export class SpSystemTrace extends BaseElement { window.subscribe(window.SmartEvent.UI.SliceMark, (data) => { this.sliceMarkEventHandler(data); }); - window.subscribe(window.SmartEvent.UI.TraceRowComplete, (tr) => {}); + window.subscribe(window.SmartEvent.UI.TraceRowComplete, (tr) => { }); window.subscribe(window.SmartEvent.UI.RefreshCanvas, () => { this.refreshCanvas(false); }); @@ -1845,7 +1854,7 @@ export class SpSystemTrace extends BaseElement { // 如果没有找到帽子,则绘制一个旗子 let time = Math.round( (x * (TraceRow.range?.endNS! - TraceRow.range?.startNS!)) / this.timerShaftEL!.canvas!.offsetWidth + - TraceRow.range?.startNS! + TraceRow.range?.startNS! ); this.timerShaftEL!.sportRuler!.drawTriangle(time, 'squre'); } @@ -2022,13 +2031,13 @@ export class SpSystemTrace extends BaseElement { this.timerShaftEL?.setSlicesMark( FrameAnimationStruct.selectFrameAnimationStruct.startTs || 0, (FrameAnimationStruct.selectFrameAnimationStruct.startTs || 0) + - (FrameAnimationStruct.selectFrameAnimationStruct.dur || 0) + (FrameAnimationStruct.selectFrameAnimationStruct.dur || 0) ); } else if (JsCpuProfilerStruct.selectJsCpuProfilerStruct) { this.timerShaftEL?.setSlicesMark( JsCpuProfilerStruct.selectJsCpuProfilerStruct.startTime || 0, (JsCpuProfilerStruct.selectJsCpuProfilerStruct.startTime || 0) + - (JsCpuProfilerStruct.selectJsCpuProfilerStruct.totalTime || 0) + (JsCpuProfilerStruct.selectJsCpuProfilerStruct.totalTime || 0) ); } else { this.slicestime = this.timerShaftEL?.setSlicesMark(); diff --git a/ide/src/trace/component/trace/base/RangeSelect.ts b/ide/src/trace/component/trace/base/RangeSelect.ts index 38521f8608a48b3f901c9782b63737b2d84b1722..06220d9270eaea14851468e9fb5a05e97552fdc9 100644 --- a/ide/src/trace/component/trace/base/RangeSelect.ts +++ b/ide/src/trace/component/trace/base/RangeSelect.ts @@ -19,6 +19,7 @@ import { ns2x, TimerShaftElement } from '../TimerShaftElement.js'; import { info } from '../../../../log/Log.js'; import './Extension.js'; import { SpSystemTrace } from '../../SpSystemTrace.js'; +import { querySearchRowFuncData } from '../../../database/SqlLite.js'; export class RangeSelect { private rowsEL: HTMLDivElement | undefined | null; @@ -79,6 +80,20 @@ export class RangeSelect { if (this.selectHandler) { this.selectHandler(this.rangeTraceRow || [], !this.isHover); } + //如果只框选了一条泳道,查询H:RSMainThread::DoComposition数据 + if (this.rangeTraceRow?.length === 1) { + querySearchRowFuncData('H:RSMainThread::DoComposition', TraceRow.currentRowId!).then((res) => { + if (res.length) { + res.forEach((item) => { + TraceRow.docompositionData.push(item.startTime!); + }); + } + }) + } else { + if (TraceRow.docompositionData.length) { + TraceRow.docompositionData = [] + } + } } this.isMouseDown = false; } diff --git a/ide/src/trace/component/trace/base/TraceRow.ts b/ide/src/trace/component/trace/base/TraceRow.ts index 17e220666840c2779176d82b46f55e15f8f1c12b..2658bc66eddc3f9cab001edb6d151a6cfb9e7bf2 100644 --- a/ide/src/trace/component/trace/base/TraceRow.ts +++ b/ide/src/trace/component/trace/base/TraceRow.ts @@ -121,6 +121,8 @@ export class TraceRow extends HTMLElement { static range: TimeRange | undefined | null; static rangeSelectObject: RangeSelectStruct | undefined; static ROW_TYPE_HI_SYSEVENT = 'hi-sysevent'; + static docompositionData: Array = []; // 存储查询到的docomposition数据 + static currentRowId: number | undefined; // 存储当前行id public obj: TraceRowObject | undefined | null; isHover: boolean = false; hoverX: number = 0; @@ -186,12 +188,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; @@ -697,7 +699,7 @@ export class TraceRow extends HTMLElement { }); } - getRowSettingKeys() : Array { + getRowSettingKeys(): Array { if (this.rowSetting === 'enable') { return this.rowSettingTree!.getCheckdKeys(); } @@ -716,7 +718,7 @@ export class TraceRow extends HTMLElement { } } - enableCollapseChart() : void { + enableCollapseChart(): void { this._enableCollapseChart = true; this.nameEL!.onclick = () => { if (this.funcExpand) { diff --git a/ide/src/trace/database/SqlLite.ts b/ide/src/trace/database/SqlLite.ts index a65756ee4478a88781cdb96af01e5e599d8e9e3d..bca598df3df0c25087da7c8761a14ffe2074044a 100644 --- a/ide/src/trace/database/SqlLite.ts +++ b/ide/src/trace/database/SqlLite.ts @@ -297,8 +297,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!); @@ -1271,8 +1271,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 } ); @@ -1684,7 +1683,7 @@ select {} ); -export const queryAllHookData = (rightNs: number,ipid: number): Promise> => +export const queryAllHookData = (rightNs: number, ipid: number): Promise> => query( 'queryAllHookData', ` @@ -3886,11 +3885,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 } @@ -5641,7 +5638,7 @@ export const queryRealTime = (): Promise< Array<{ ts: number }> - > => +> => query( 'queryRealTime', `select CS.ts as ts from clock_snapshot as CS where clock_name = 'realtime';` @@ -5674,3 +5671,37 @@ export const queryHiSysEventData = (): Promise> => LEFT JOIN data_dict AS D2 on S.domain_id = D2.id ORDER BY S.ts` ); + +export const querySearchRowFuncData = (funcName: string, tIds: number): Promise> => + query( + 'querySearchFuncData', + ` + select + c.cookie, + c.id, + c.name as funName, + c.ts - r.start_ts as startTime, + c.dur,c.depth, + t.tid, + t.name as threadName, + p.pid, + 'func' as type + 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}; + `, + { $search: funcName } + ); diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerCommon.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerCommon.ts index f4165328d29a7a3c051d8590c4294389ba958b97..fad78431881d5c9ea3b2cbeee1e356ba397b15e8 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; @@ -537,10 +537,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) { @@ -742,6 +742,99 @@ export function drawSelectionRange(context: any, params: TraceRow) { ); context.globalAlpha = 1; } + + // 绘制方法H:RSMainThread::DoComposition平均帧率的箭头指示线条 + if (TraceRow.docompositionData.length && params.getAttribute('row-type') === 'func') { + let rateList: Array = TraceRow.docompositionData.filter(function (item: number) { + return item >= TraceRow.rangeSelectObject!.startNS! && item <= TraceRow.rangeSelectObject!.endNS! + }) + rateList = [...new Set(rateList)]; + 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); + + 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); + } + } } } @@ -1047,7 +1140,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) {