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',
|
color: '#EC407A',
|
||||||
downloadArchiveDir: 'transition',
|
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 {FunctionUtils, OnProgressUpdateType} from 'common/function_utils';
|
||||||
import {ParserError, ParserFactory} from 'parsers/parser_factory';
|
import {ParserError, ParserFactory} from 'parsers/parser_factory';
|
||||||
|
import {TracesParserTransitions} from 'parsers/traces_parser_transitions';
|
||||||
import {FrameMapper} from 'trace/frame_mapper';
|
import {FrameMapper} from 'trace/frame_mapper';
|
||||||
import {LoadedTrace} from 'trace/loaded_trace';
|
import {LoadedTrace} from 'trace/loaded_trace';
|
||||||
import {Parser} from 'trace/parser';
|
import {Parser} from 'trace/parser';
|
||||||
@@ -42,10 +43,23 @@ class TracePipeline {
|
|||||||
);
|
);
|
||||||
this.parsers = parsers.map((it) => it.parser);
|
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) {
|
for (const parser of parsers) {
|
||||||
this.files.set(parser.parser.getTraceType(), parser.file);
|
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;
|
return parserErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +83,10 @@ class TracePipeline {
|
|||||||
this.traces = new Traces();
|
this.traces = new Traces();
|
||||||
this.parsers.forEach((parser) => {
|
this.parsers.forEach((parser) => {
|
||||||
const trace = Trace.newUninitializedTrace(parser);
|
const trace = Trace.newUninitializedTrace(parser);
|
||||||
|
trace.init(commonTimestampType);
|
||||||
this.traces?.setTrace(parser.getTraceType(), trace);
|
this.traces?.setTrace(parser.getTraceType(), trace);
|
||||||
});
|
});
|
||||||
|
|
||||||
new FrameMapper(this.traces).computeMapping();
|
new FrameMapper(this.traces).computeMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ abstract class AbstractParser implements Parser<object> {
|
|||||||
throw TypeError("buffer doesn't contain expected magic number");
|
throw TypeError("buffer doesn't contain expected magic number");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.decodedEntries = this.decodeTrace(traceBuffer).map((it) => this.addDefaultProtoFields(it));
|
this.decodedEntries = this.decodeTrace(traceBuffer).map((it) => this.addDefaultProtoFields(it));
|
||||||
|
|
||||||
for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import {Event} from 'trace/flickerlib/common';
|
import {Event, Transition} from 'trace/flickerlib/common';
|
||||||
import {Transition} from './flickerlib/common';
|
|
||||||
import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry';
|
import {LayerTraceEntry} from './flickerlib/layers/LayerTraceEntry';
|
||||||
import {WindowManagerState} from './flickerlib/windows/WindowManagerState';
|
import {WindowManagerState} from './flickerlib/windows/WindowManagerState';
|
||||||
import {LogMessage} from './protolog';
|
import {LogMessage} from './protolog';
|
||||||
@@ -38,6 +37,7 @@ export enum TraceType {
|
|||||||
EVENT_LOG,
|
EVENT_LOG,
|
||||||
WM_TRANSITION,
|
WM_TRANSITION,
|
||||||
SHELL_TRANSITION,
|
SHELL_TRANSITION,
|
||||||
|
TRANSITION,
|
||||||
TAG,
|
TAG,
|
||||||
ERROR,
|
ERROR,
|
||||||
TEST_TRACE_STRING,
|
TEST_TRACE_STRING,
|
||||||
@@ -62,6 +62,7 @@ export interface TraceEntryTypeMap {
|
|||||||
[TraceType.EVENT_LOG]: Event;
|
[TraceType.EVENT_LOG]: Event;
|
||||||
[TraceType.WM_TRANSITION]: object;
|
[TraceType.WM_TRANSITION]: object;
|
||||||
[TraceType.SHELL_TRANSITION]: object;
|
[TraceType.SHELL_TRANSITION]: object;
|
||||||
|
[TraceType.TRANSITION]: Transition;
|
||||||
[TraceType.TAG]: object;
|
[TraceType.TAG]: object;
|
||||||
[TraceType.ERROR]: object;
|
[TraceType.ERROR]: object;
|
||||||
[TraceType.TEST_TRACE_STRING]: string;
|
[TraceType.TEST_TRACE_STRING]: string;
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export class Traces {
|
|||||||
return this.traces.get(type) as Trace<TraceEntryTypeMap[T]> | undefined;
|
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 {
|
sliceTime(start?: Timestamp, end?: Timestamp): Traces {
|
||||||
const slice = new Traces();
|
const slice = new Traces();
|
||||||
this.traces.forEach((trace, type) => {
|
this.traces.forEach((trace, type) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user