From 4cb72593d068b469916fd798f95b76fcdb996803 Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Wed, 5 Apr 2023 14:05:09 +0000 Subject: [PATCH] Implement Transition trace parser in Winscope Bug: 265791162 Test: npm run build:all && npm run test:all Change-Id: Ib37d722079ed75f7c1b75cba506757b6c41f41aa --- tools/winscope/src/parsers/parser_factory.ts | 2 + .../src/parsers/parser_transitions.ts | 122 ++++++++++++++++++ .../src/parsers/parser_transitions_test.ts | 50 +++++++ tools/winscope/src/parsers/proto_types.js | 5 + .../traces/elapsed_timestamp/Transitions.pb | 9 ++ tools/winscope/src/trace/flickerlib/common.js | 15 +++ tools/winscope/src/trace/trace_type.ts | 3 + 7 files changed, 206 insertions(+) create mode 100644 tools/winscope/src/parsers/parser_transitions.ts create mode 100644 tools/winscope/src/parsers/parser_transitions_test.ts create mode 100644 tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb diff --git a/tools/winscope/src/parsers/parser_factory.ts b/tools/winscope/src/parsers/parser_factory.ts index fa8cbd139..836735ba6 100644 --- a/tools/winscope/src/parsers/parser_factory.ts +++ b/tools/winscope/src/parsers/parser_factory.ts @@ -28,6 +28,7 @@ import {ParserScreenRecording} from './parser_screen_recording'; import {ParserScreenRecordingLegacy} from './parser_screen_recording_legacy'; import {ParserSurfaceFlinger} from './parser_surface_flinger'; import {ParserTransactions} from './parser_transactions'; +import {ParserTransitions} from './parser_transitions'; import {ParserWindowManager} from './parser_window_manager'; import {ParserWindowManagerDump} from './parser_window_manager_dump'; @@ -45,6 +46,7 @@ export class ParserFactory { ParserWindowManager, ParserWindowManagerDump, ParserEventLog, + ParserTransitions, ]; private parsers = new Map>(); diff --git a/tools/winscope/src/parsers/parser_transitions.ts b/tools/winscope/src/parsers/parser_transitions.ts new file mode 100644 index 000000000..f82f2d4b1 --- /dev/null +++ b/tools/winscope/src/parsers/parser_transitions.ts @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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 { + CrossPlatform, + Transition, + TransitionChange, + TransitionType, + WindowingMode, +} from 'trace/flickerlib/common'; +import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp'; +import {TraceFile} from 'trace/trace_file'; +import {TraceType} from 'trace/trace_type'; +import {AbstractParser} from './abstract_parser'; +import {TransitionsTraceFileProto} from './proto_types'; + +export class ParserTransitions extends AbstractParser { + constructor(trace: TraceFile) { + super(trace); + this.realToElapsedTimeOffsetNs = undefined; + } + + override getTraceType(): TraceType { + return TraceType.TRANSITION; + } + + override getMagicNumber(): number[] { + return ParserTransitions.MAGIC_NUMBER; + } + + override decodeTrace(buffer: Uint8Array): any[] { + const decodedProto = TransitionsTraceFileProto.decode(buffer) as any; + this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos); + + return decodedProto.transitions; + } + + override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp { + if (type === TimestampType.ELAPSED) { + return new ElapsedTimestamp(BigInt(entryProto.createTimeNs)); + } + if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) { + return new RealTimestamp(this.realToElapsedTimeOffsetNs + BigInt(entryProto.createTimeNs)); + } + return undefined; + } + + override processDecodedEntry( + index: number, + timestampType: TimestampType, + entryProto: any + ): Transition { + if (!this.transitions) { + const transitions = this.decodedEntries.map((it) => this.parseProto(it)); + this.transitions = transitions; + } + return this.transitions[index]; + } + + private parseProto(entryProto: any): Transition { + const changes = entryProto.targets.map((it: any) => { + const windowingMode = WindowingMode.WINDOWING_MODE_UNDEFINED; // TODO: Get the windowing mode + + return new TransitionChange( + TransitionType.Companion.fromInt(it.mode), + it.layerId, + it.windowId, + windowingMode + ); + }); + + const createTime = CrossPlatform.timestamp.fromString( + entryProto.createTimeNs.toString(), + null, + null + ); + const sendTime = CrossPlatform.timestamp.fromString( + entryProto.sendTimeNs.toString(), + null, + null + ); + const finishTime = CrossPlatform.timestamp.fromString( + entryProto.finishTimeNs.toString(), + null, + null + ); + const startTransactionId = entryProto.startTransactionId; + const finishTransactionId = entryProto.finishTransactionId; + const type = TransitionType.Companion.fromInt(entryProto.type); + const played = entryProto.finishTimeNs > 0; + const aborted = entryProto.sendTimeNs === 0; + + return new Transition( + createTime, + sendTime, + finishTime, + startTransactionId, + finishTransactionId, + type, + changes, + played, + aborted + ); + } + + private transitions: Transition[] | undefined; + private realToElapsedTimeOffsetNs: undefined | bigint; + private static readonly MAGIC_NUMBER = [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE +} diff --git a/tools/winscope/src/parsers/parser_transitions_test.ts b/tools/winscope/src/parsers/parser_transitions_test.ts new file mode 100644 index 000000000..27b6642b4 --- /dev/null +++ b/tools/winscope/src/parsers/parser_transitions_test.ts @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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 {UnitTestUtils} from 'test/unit/utils'; +import {Parser} from 'trace/parser'; +import {ElapsedTimestamp, TimestampType} from 'trace/timestamp'; +import {TraceType} from 'trace/trace_type'; + +describe('ParserTransitions', () => { + describe('trace with elapsed (only) timestamp', () => { + let parser: Parser; + + beforeAll(async () => { + parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/Transitions.pb'); + }); + + it('has expected trace type', () => { + expect(parser.getTraceType()).toEqual(TraceType.TRANSITION); + }); + + it('provides elapsed timestamps', () => { + const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!; + + expect(timestamps.length).toEqual(7); + + const expected = [ + new ElapsedTimestamp(1862299518404n), + new ElapsedTimestamp(1863412780164n), + new ElapsedTimestamp(1865439877129n), + ]; + expect(timestamps.slice(0, 3)).toEqual(expected); + }); + + it("doesn't provide real timestamps", () => { + expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined); + }); + }); +}); diff --git a/tools/winscope/src/parsers/proto_types.js b/tools/winscope/src/parsers/proto_types.js index 331667891..9ab722600 100644 --- a/tools/winscope/src/parsers/proto_types.js +++ b/tools/winscope/src/parsers/proto_types.js @@ -22,6 +22,7 @@ protobuf.configure(); import protoLogJson from 'frameworks/base/core/proto/android/internal/protolog.proto'; import accessibilityJson from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto'; import windowManagerJson from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'; +import transitionsJson from 'frameworks/base/core/proto/android/server/windowmanagertransitiontrace.proto'; import inputMethodClientsJson from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto'; import layersJson from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto'; import transactionsJson from 'frameworks/native/services/surfaceflinger/layerproto/transactions.proto'; @@ -53,6 +54,9 @@ const WindowManagerServiceDumpProto = protobuf.Root.fromJSON(windowManagerJson). const WindowManagerTraceFileProto = protobuf.Root.fromJSON(windowManagerJson).lookupType( 'com.android.server.wm.WindowManagerTraceFileProto' ); +const TransitionsTraceFileProto = protobuf.Root.fromJSON(transitionsJson).lookupType( + 'com.android.server.wm.shell.TransitionTraceProto' +); export { AccessibilityTraceFileProto, @@ -64,4 +68,5 @@ export { TransactionsTraceFileProto, WindowManagerServiceDumpProto, WindowManagerTraceFileProto, + TransitionsTraceFileProto, }; diff --git a/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb new file mode 100644 index 000000000..c639db402 --- /dev/null +++ b/tools/winscope/src/test/fixtures/traces/elapsed_timestamp/Transitions.pb @@ -0,0 +1,9 @@ + TRNTRACE@)8B +B . ċ͙6(Ԏٙ60ۛ6@*8B .B + 6(606@+8B +ת-B . 6(606@ﴀ,8B .B +ת- Ы6(606@-8B +vB . ɴʴ6(״60Ҷ6A.8B +oB +r ѸȈ6(606@/8B .B +v 6(60꼪6 \ No newline at end of file diff --git a/tools/winscope/src/trace/flickerlib/common.js b/tools/winscope/src/trace/flickerlib/common.js index 9b66fdb5e..5d4f01ddb 100644 --- a/tools/winscope/src/trace/flickerlib/common.js +++ b/tools/winscope/src/trace/flickerlib/common.js @@ -63,6 +63,11 @@ const FlickerEvent = require('flicker').android.tools.common.traces.events.Flick const FocusEvent = require('flicker').android.tools.common.traces.events.FocusEvent; const EventLogParser = require('flicker').android.tools.common.parsers.events.EventLogParser; +// Transitions +const Transition = require('flicker').android.tools.common.traces.wm.Transition; +const TransitionType = require('flicker').android.tools.common.traces.wm.TransitionType; +const TransitionChange = require('flicker').android.tools.common.traces.wm.TransitionChange; + // Common const Size = require('flicker').android.tools.common.datatypes.Size; const ActiveBuffer = require('flicker').android.tools.common.datatypes.ActiveBuffer; @@ -77,6 +82,9 @@ const Point = require('flicker').android.tools.common.datatypes.Point; const PointF = require('flicker').android.tools.common.datatypes.PointF; const Rect = require('flicker').android.tools.common.datatypes.Rect; const RectF = require('flicker').android.tools.common.datatypes.RectF; +const WindowingMode = require('flicker').android.tools.common.traces.wm.WindowingMode; +const CrossPlatform = require('flicker').android.tools.common.CrossPlatform; +const TimestampFactory = require('flicker').android.tools.common.TimestampFactory; const EMPTY_SIZE = Size.Companion.EMPTY; const EMPTY_BUFFER = ActiveBuffer.Companion.EMPTY; @@ -302,6 +310,10 @@ export { FlickerEvent, FocusEvent, EventLogParser, + // Transitions + Transition, + TransitionType, + TransitionChange, // Common Size, ActiveBuffer, @@ -314,6 +326,9 @@ export { RectF, Region, Rotation, + WindowingMode, + CrossPlatform, + TimestampFactory, // Service toSize, toActiveBuffer, diff --git a/tools/winscope/src/trace/trace_type.ts b/tools/winscope/src/trace/trace_type.ts index f03252053..c721941ea 100644 --- a/tools/winscope/src/trace/trace_type.ts +++ b/tools/winscope/src/trace/trace_type.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import {Event} from 'trace/flickerlib/common'; +import {Transition} from './flickerlib/common'; import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry'; import {WindowManagerState} from './flickerlib/windows/WindowManagerState'; import {LogMessage} from './protolog'; @@ -35,6 +36,7 @@ export enum TraceType { INPUT_METHOD_MANAGER_SERVICE, INPUT_METHOD_SERVICE, EVENT_LOG, + TRANSITION, TAG, ERROR, TEST_TRACE_STRING, @@ -57,6 +59,7 @@ export interface TraceEntryTypeMap { [TraceType.INPUT_METHOD_MANAGER_SERVICE]: object; [TraceType.INPUT_METHOD_SERVICE]: object; [TraceType.EVENT_LOG]: Event; + [TraceType.TRANSITION]: Transition; [TraceType.TAG]: object; [TraceType.ERROR]: object; [TraceType.TEST_TRACE_STRING]: string;