From 9336f610f0b451a18f89330daa73e2bee14adae0 Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Thu, 1 Jun 2023 16:49:33 +0000 Subject: [PATCH] Add CUJ trace parser to Winscope Bug: 265791272 Test: npm run test:all Change-Id: Ibb62236c405ab44caf94c9ac66ef34cbef4a0111 --- tools/winscope/src/app/trace_info.ts | 7 ++ tools/winscope/src/app/trace_pipeline.ts | 12 ++- tools/winscope/src/parsers/abstract_parser.ts | 4 +- tools/winscope/src/parsers/parser_eventlog.ts | 2 +- .../src/parsers/traces_parser_cujs.ts | 90 +++++++++++++++++++ .../src/parsers/traces_parser_cujs_test.ts | 66 ++++++++++++++ tools/winscope/src/trace/flickerlib/common.js | 4 + tools/winscope/src/trace/trace_type.ts | 4 +- 8 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 tools/winscope/src/parsers/traces_parser_cujs.ts create mode 100644 tools/winscope/src/parsers/traces_parser_cujs_test.ts diff --git a/tools/winscope/src/app/trace_info.ts b/tools/winscope/src/app/trace_info.ts index 5cad268bd..af238bd5d 100644 --- a/tools/winscope/src/app/trace_info.ts +++ b/tools/winscope/src/app/trace_info.ts @@ -30,6 +30,7 @@ const TAG_ICON = 'details'; const TRACE_ERROR_ICON = 'warning'; const EVENT_LOG_ICON = 'description'; const TRANSITION_ICON = 'animation'; +const CUJ_ICON = 'label'; interface TraceInfoMap { [key: number]: { @@ -161,4 +162,10 @@ export const TRACE_INFO: TraceInfoMap = { color: '#EC407A', downloadArchiveDir: 'transition', }, + [TraceType.CUJS]: { + name: 'Cujs', + icon: CUJ_ICON, + color: '#EC407A', + downloadArchiveDir: 'eventlog', + }, }; diff --git a/tools/winscope/src/app/trace_pipeline.ts b/tools/winscope/src/app/trace_pipeline.ts index 8ab16b70f..8a9a472c0 100644 --- a/tools/winscope/src/app/trace_pipeline.ts +++ b/tools/winscope/src/app/trace_pipeline.ts @@ -16,6 +16,7 @@ import {FunctionUtils, OnProgressUpdateType} from 'common/function_utils'; import {ParserError, ParserFactory} from 'parsers/parser_factory'; +import {TracesParserCujs} from 'parsers/traces_parser_cujs'; import {TracesParserTransitions} from 'parsers/traces_parser_transitions'; import {FrameMapper} from 'trace/frame_mapper'; import {LoadedTrace} from 'trace/loaded_trace'; @@ -43,9 +44,14 @@ class TracePipeline { ); this.parsers = parsers.map((it) => it.parser); - const tracesParser = new TracesParserTransitions(this.parsers); - if (tracesParser.canProvideEntries()) { - this.parsers.push(tracesParser); + const tracesParsers = [ + new TracesParserTransitions(this.parsers), + new TracesParserCujs(this.parsers), + ]; + for (const tracesParser of tracesParsers) { + if (tracesParser.canProvideEntries()) { + this.parsers.push(tracesParser); + } } for (const parser of parsers) { diff --git a/tools/winscope/src/parsers/abstract_parser.ts b/tools/winscope/src/parsers/abstract_parser.ts index 91e2dea72..60637423b 100644 --- a/tools/winscope/src/parsers/abstract_parser.ts +++ b/tools/winscope/src/parsers/abstract_parser.ts @@ -20,7 +20,7 @@ import {Timestamp, TimestampType} from 'trace/timestamp'; import {TraceFile} from 'trace/trace_file'; import {TraceType} from 'trace/trace_type'; -abstract class AbstractParser implements Parser { +abstract class AbstractParser implements Parser { protected traceFile: TraceFile; protected decodedEntries: any[] = []; private timestamps: Map = new Map(); @@ -78,7 +78,7 @@ abstract class AbstractParser implements Parser { return this.timestamps.get(type); } - getEntry(index: number, timestampType: TimestampType): object { + getEntry(index: number, timestampType: TimestampType): T { return this.processDecodedEntry(index, timestampType, this.decodedEntries[index]); } diff --git a/tools/winscope/src/parsers/parser_eventlog.ts b/tools/winscope/src/parsers/parser_eventlog.ts index 629c9435f..902e80c49 100644 --- a/tools/winscope/src/parsers/parser_eventlog.ts +++ b/tools/winscope/src/parsers/parser_eventlog.ts @@ -19,7 +19,7 @@ import {RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp'; import {TraceType} from 'trace/trace_type'; import {AbstractParser} from './abstract_parser'; -class ParserEventLog extends AbstractParser { +class ParserEventLog extends AbstractParser { override getTraceType(): TraceType { return TraceType.EVENT_LOG; } diff --git a/tools/winscope/src/parsers/traces_parser_cujs.ts b/tools/winscope/src/parsers/traces_parser_cujs.ts new file mode 100644 index 000000000..cd82eed5e --- /dev/null +++ b/tools/winscope/src/parsers/traces_parser_cujs.ts @@ -0,0 +1,90 @@ +/* + * Copyright 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 {Cuj, CujTrace, EventLog, Transition} from 'trace/flickerlib/common'; +import {Parser} from 'trace/parser'; +import {Timestamp, TimestampType} from 'trace/timestamp'; +import {TraceType} from 'trace/trace_type'; +import {AbstractTracesParser} from './abstract_traces_parser'; +import {ParserEventLog} from './parser_eventlog'; + +export class TracesParserCujs extends AbstractTracesParser { + private readonly eventLogTrace: ParserEventLog | undefined; + private readonly descriptors: string[]; + + constructor(parsers: Array>) { + super(parsers); + + const eventlogTraces = this.parsers.filter((it) => it.getTraceType() === TraceType.EVENT_LOG); + if (eventlogTraces.length > 0) { + this.eventLogTrace = eventlogTraces[0] as ParserEventLog; + } + + if (this.eventLogTrace !== undefined) { + this.descriptors = this.eventLogTrace.getDescriptors(); + } else { + this.descriptors = []; + } + } + + override canProvideEntries(): boolean { + return this.eventLogTrace !== undefined; + } + + getLengthEntries(): number { + return this.getDecodedEntries().length; + } + + getEntry(index: number, timestampType: TimestampType): Transition { + return this.getDecodedEntries()[index]; + } + + private cujTrace: CujTrace | undefined; + getDecodedEntries(): Cuj[] { + if (this.eventLogTrace === undefined) { + throw new Error('eventLogTrace not defined'); + } + + if (this.cujTrace === undefined) { + const events: Event[] = []; + + for (let i = 0; i < this.eventLogTrace.getLengthEntries(); i++) { + events.push(this.eventLogTrace.getEntry(i, TimestampType.REAL)); + } + + this.cujTrace = new EventLog(events).cujTrace; + } + + return this.cujTrace.entries; + } + + override getDescriptors(): string[] { + return this.descriptors; + } + + getTraceType(): TraceType { + return TraceType.CUJS; + } + + override getTimestamp(type: TimestampType, transition: Transition): undefined | Timestamp { + if (type === TimestampType.ELAPSED) { + return new Timestamp(type, BigInt(transition.timestamp.elapsedNanos.toString())); + } else if (type === TimestampType.REAL) { + return new Timestamp(type, BigInt(transition.timestamp.unixNanos.toString())); + } + return undefined; + } +} diff --git a/tools/winscope/src/parsers/traces_parser_cujs_test.ts b/tools/winscope/src/parsers/traces_parser_cujs_test.ts new file mode 100644 index 000000000..41b0997af --- /dev/null +++ b/tools/winscope/src/parsers/traces_parser_cujs_test.ts @@ -0,0 +1,66 @@ +/* + * 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 {assertDefined} from 'common/assert_utils'; +import {UnitTestUtils} from 'test/unit/utils'; +import {Cuj} from 'trace/flickerlib/common'; +import {Parser} from 'trace/parser'; +import {Timestamp, TimestampType} from 'trace/timestamp'; +import {TraceType} from 'trace/trace_type'; +import {TracesParserCujs} from './traces_parser_cujs'; + +describe('ParserCujs', () => { + let parser: Parser; + + beforeAll(async () => { + const eventLogParser = assertDefined( + await UnitTestUtils.getParser('traces/eventlog.winscope') + ) as Parser; + + parser = new TracesParserCujs([eventLogParser]); + }); + + it('has expected trace type', () => { + expect(parser.getTraceType()).toEqual(TraceType.CUJS); + }); + + it('provides elapsed timestamps', () => { + const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!; + + expect(timestamps.length).toEqual(16); + + const expected = [ + new Timestamp(TimestampType.ELAPSED, 2661012770462n), + new Timestamp(TimestampType.ELAPSED, 2661012874914n), + new Timestamp(TimestampType.ELAPSED, 2661012903966n), + ]; + expect(timestamps.slice(0, 3)).toEqual(expected); + }); + + it('provides real timestamps', () => { + const expected = [ + new Timestamp(TimestampType.REAL, 1681207048025446000n), + new Timestamp(TimestampType.REAL, 1681207048025551000n), + new Timestamp(TimestampType.REAL, 1681207048025580000n), + ]; + + const timestamps = parser.getTimestamps(TimestampType.REAL)!; + + expect(timestamps.length).toEqual(16); + + expect(timestamps.slice(0, 3)).toEqual(expected); + }); +}); diff --git a/tools/winscope/src/trace/flickerlib/common.js b/tools/winscope/src/trace/flickerlib/common.js index 7cce4678d..278e26bb0 100644 --- a/tools/winscope/src/trace/flickerlib/common.js +++ b/tools/winscope/src/trace/flickerlib/common.js @@ -62,6 +62,8 @@ const Event = require('flicker').android.tools.common.traces.events.Event; const FlickerEvent = require('flicker').android.tools.common.traces.events.FlickerEvent; const FocusEvent = require('flicker').android.tools.common.traces.events.FocusEvent; const EventLogParser = require('flicker').android.tools.common.parsers.events.EventLogParser; +const CujTrace = require('flicker').android.tools.common.parsers.events.CujTrace; +const Cuj = require('flicker').android.tools.common.parsers.events.Cuj; // Transitions const Transition = require('flicker').android.tools.common.traces.wm.Transition; @@ -313,6 +315,8 @@ export { FlickerEvent, FocusEvent, EventLogParser, + CujTrace, + Cuj, // Transitions Transition, TransitionType, diff --git a/tools/winscope/src/trace/trace_type.ts b/tools/winscope/src/trace/trace_type.ts index 9d1277f51..7264af70a 100644 --- a/tools/winscope/src/trace/trace_type.ts +++ b/tools/winscope/src/trace/trace_type.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {Event, Transition} from 'trace/flickerlib/common'; +import {Cuj, Event, Transition} from 'trace/flickerlib/common'; import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry'; import {WindowManagerState} from './flickerlib/windows/WindowManagerState'; import {LogMessage} from './protolog'; @@ -38,6 +38,7 @@ export enum TraceType { WM_TRANSITION, SHELL_TRANSITION, TRANSITION, + CUJS, TAG, ERROR, TEST_TRACE_STRING, @@ -63,6 +64,7 @@ export interface TraceEntryTypeMap { [TraceType.WM_TRANSITION]: object; [TraceType.SHELL_TRANSITION]: object; [TraceType.TRANSITION]: Transition; + [TraceType.CUJS]: Cuj; [TraceType.TAG]: object; [TraceType.ERROR]: object; [TraceType.TEST_TRACE_STRING]: string;