Add transition traces parser

Bug: 277181336
Test: npm run test:all
Change-Id: I7eb981367191c47d3b50b31ee66f147ff1d6923c
This commit is contained in:
Pablo Gamito
2023-05-22 10:24:37 +00:00
parent 99afc1013b
commit a735b202f7
8 changed files with 284 additions and 3 deletions

View File

@@ -155,4 +155,10 @@ export const TRACE_INFO: TraceInfoMap = {
color: '#EC407A',
downloadArchiveDir: 'transition',
},
[TraceType.TRANSITION]: {
name: 'Transitions',
icon: TRANSITION_ICON,
color: '#EC407A',
downloadArchiveDir: 'transition',
},
};

View File

@@ -16,6 +16,7 @@
import {FunctionUtils, OnProgressUpdateType} from 'common/function_utils';
import {ParserError, ParserFactory} from 'parsers/parser_factory';
import {TracesParserTransitions} from 'parsers/traces_parser_transitions';
import {FrameMapper} from 'trace/frame_mapper';
import {LoadedTrace} from 'trace/loaded_trace';
import {Parser} from 'trace/parser';
@@ -42,10 +43,23 @@ class TracePipeline {
);
this.parsers = parsers.map((it) => it.parser);
const tracesParser = new TracesParserTransitions(this.parsers);
if (tracesParser.canProvideEntries()) {
this.parsers.push(tracesParser);
}
for (const parser of parsers) {
this.files.set(parser.parser.getTraceType(), parser.file);
}
if (this.parsers.some((it) => it.getTraceType() === TraceType.TRANSITION)) {
this.parsers = this.parsers.filter(
(it) =>
it.getTraceType() !== TraceType.WM_TRANSITION &&
it.getTraceType() !== TraceType.SHELL_TRANSITION
);
}
return parserErrors;
}
@@ -69,8 +83,10 @@ class TracePipeline {
this.traces = new Traces();
this.parsers.forEach((parser) => {
const trace = Trace.newUninitializedTrace(parser);
trace.init(commonTimestampType);
this.traces?.setTrace(parser.getTraceType(), trace);
});
new FrameMapper(this.traces).computeMapping();
}

View File

@@ -42,7 +42,7 @@ abstract class AbstractParser implements Parser<object> {
throw TypeError("buffer doesn't contain expected magic number");
}
}
this.decodedEntries = this.decodeTrace(traceBuffer).map((it) => this.addDefaultProtoFields(it));
for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {

View File

@@ -0,0 +1,75 @@
/*
* 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 {Parser} from 'trace/parser';
import {Timestamp, TimestampType} from 'trace/timestamp';
import {TraceFile} from 'trace/trace_file';
import {TraceType} from 'trace/trace_type';
export abstract class AbstractTracesParser<T> implements Parser<T> {
constructor(readonly parsers: Array<Parser<object>>) {}
getTraceFile(): TraceFile {
throw new Error('Method not implemented.');
}
abstract canProvideEntries(): boolean;
abstract getDescriptors(): string[];
abstract getTraceType(): TraceType;
abstract getEntry(index: number, timestampType: TimestampType): T;
abstract getLengthEntries(): number;
getTimestamps(type: TimestampType): Timestamp[] | undefined {
this.setTimestamps();
return this.timestamps.get(type);
}
private setTimestamps() {
if (this.timestampsSet) {
return;
}
for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
const timestamps: Timestamp[] = [];
let areTimestampsValid = true;
for (let index = 0; index < this.getLengthEntries(); index++) {
const entry = this.getEntry(index, type);
const timestamp = this.getTimestamp(type, entry);
if (timestamp === undefined) {
areTimestampsValid = false;
break;
}
timestamps.push(timestamp);
}
if (areTimestampsValid) {
this.timestamps.set(type, timestamps);
}
}
this.timestampsSet = true;
}
abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined | Timestamp;
private timestampsSet: boolean = false;
private timestamps: Map<TimestampType, Timestamp[]> = new Map<TimestampType, Timestamp[]>();
}

View File

@@ -0,0 +1,111 @@
/*
* 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 {Transition, TransitionsTrace} 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';
export class TracesParserTransitions extends AbstractTracesParser<Transition> {
private readonly wmTransitionTrace: Parser<object> | undefined;
private readonly shellTransitionTrace: Parser<object> | undefined;
private readonly descriptors: string[];
constructor(parsers: Array<Parser<object>>) {
super(parsers);
const wmTransitionTraces = this.parsers.filter(
(it) => it.getTraceType() === TraceType.WM_TRANSITION
);
if (wmTransitionTraces.length > 0) {
this.wmTransitionTrace = wmTransitionTraces[0];
}
const shellTransitionTraces = this.parsers.filter(
(it) => it.getTraceType() === TraceType.SHELL_TRANSITION
);
if (shellTransitionTraces.length > 0) {
this.shellTransitionTrace = shellTransitionTraces[0];
}
if (this.wmTransitionTrace !== undefined && this.shellTransitionTrace !== undefined) {
this.descriptors = this.wmTransitionTrace
.getDescriptors()
.concat(this.shellTransitionTrace.getDescriptors());
} else {
this.descriptors = [];
}
}
override canProvideEntries(): boolean {
return this.wmTransitionTrace !== undefined && this.shellTransitionTrace !== undefined;
}
getLengthEntries(): number {
return this.getDecodedEntries().length;
}
getEntry(index: number, timestampType: TimestampType): Transition {
return this.getDecodedEntries()[index];
}
private decodedEntries: Transition[] | undefined;
getDecodedEntries(): Transition[] {
if (this.decodedEntries === undefined) {
if (this.wmTransitionTrace === undefined) {
throw new Error('Missing WM Transition trace');
}
if (this.shellTransitionTrace === undefined) {
throw new Error('Missing Shell Transition trace');
}
const wmTransitionEntries: Transition[] = [];
for (let index = 0; index < this.wmTransitionTrace.getLengthEntries(); index++) {
wmTransitionEntries.push(this.wmTransitionTrace.getEntry(index, TimestampType.REAL));
}
const shellTransitionEntries: Transition[] = [];
for (let index = 0; index < this.shellTransitionTrace.getLengthEntries(); index++) {
shellTransitionEntries.push(this.shellTransitionTrace.getEntry(index, TimestampType.REAL));
}
const transitionsTrace = new TransitionsTrace(
wmTransitionEntries.concat(shellTransitionEntries)
);
this.decodedEntries = transitionsTrace.asCompressed().entries as Transition[];
}
return this.decodedEntries;
}
override getDescriptors(): string[] {
return this.descriptors;
}
getTraceType(): TraceType {
return TraceType.TRANSITION;
}
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;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 {Transition} from 'trace/flickerlib/common';
import {Parser} from 'trace/parser';
import {Timestamp, TimestampType} from 'trace/timestamp';
import {TraceType} from 'trace/trace_type';
import {TracesParserTransitions} from './traces_parser_transitions';
describe('ParserTransitions', () => {
let parser: Parser<Transition>;
beforeAll(async () => {
const wmSideParser = await UnitTestUtils.getParser(
'traces/elapsed_and_real_timestamp/wm_transition_trace.pb'
);
const shellSideParser = await UnitTestUtils.getParser(
'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
);
parser = new TracesParserTransitions([wmSideParser, shellSideParser]);
});
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(4);
const expected = [
new Timestamp(TimestampType.ELAPSED, 57649586217344n),
new Timestamp(TimestampType.ELAPSED, 57649691956439n),
new Timestamp(TimestampType.ELAPSED, 57651263812071n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
it('provides real timestamps', () => {
const expected = [
new Timestamp(TimestampType.REAL, 1683188477542869667n),
new Timestamp(TimestampType.REAL, 1683188477648608762n),
new Timestamp(TimestampType.REAL, 1683188479220464394n),
];
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
expect(timestamps.length).toEqual(4);
expect(timestamps.slice(0, 3)).toEqual(expected);
});
});

View File

@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Event} from 'trace/flickerlib/common';
import {Transition} from './flickerlib/common';
import {Event, Transition} from 'trace/flickerlib/common';
import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry';
import {WindowManagerState} from './flickerlib/windows/WindowManagerState';
import {LogMessage} from './protolog';
@@ -38,6 +37,7 @@ export enum TraceType {
EVENT_LOG,
WM_TRANSITION,
SHELL_TRANSITION,
TRANSITION,
TAG,
ERROR,
TEST_TRACE_STRING,
@@ -62,6 +62,7 @@ export interface TraceEntryTypeMap {
[TraceType.EVENT_LOG]: Event;
[TraceType.WM_TRANSITION]: object;
[TraceType.SHELL_TRANSITION]: object;
[TraceType.TRANSITION]: Transition;
[TraceType.TAG]: object;
[TraceType.ERROR]: object;
[TraceType.TEST_TRACE_STRING]: string;

View File

@@ -30,6 +30,10 @@ export class Traces {
return this.traces.get(type) as Trace<TraceEntryTypeMap[T]> | undefined;
}
deleteTrace<T extends TraceType>(type: T) {
this.traces.delete(type);
}
sliceTime(start?: Timestamp, end?: Timestamp): Traces {
const slice = new Traces();
this.traces.forEach((trace, type) => {