Add transition traces parser
Bug: 277181336 Test: npm run test:all Change-Id: I7eb981367191c47d3b50b31ee66f147ff1d6923c
This commit is contained in:
@@ -155,4 +155,10 @@ export const TRACE_INFO: TraceInfoMap = {
|
||||
color: '#EC407A',
|
||||
downloadArchiveDir: 'transition',
|
||||
},
|
||||
[TraceType.TRANSITION]: {
|
||||
name: 'Transitions',
|
||||
icon: TRANSITION_ICON,
|
||||
color: '#EC407A',
|
||||
downloadArchiveDir: 'transition',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
75
tools/winscope/src/parsers/abstract_traces_parser.ts
Normal file
75
tools/winscope/src/parsers/abstract_traces_parser.ts
Normal 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[]>();
|
||||
}
|
||||
111
tools/winscope/src/parsers/traces_parser_transitions.ts
Normal file
111
tools/winscope/src/parsers/traces_parser_transitions.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
68
tools/winscope/src/parsers/traces_parser_transitions_test.ts
Normal file
68
tools/winscope/src/parsers/traces_parser_transitions_test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user